6dc4183406a4dff3f8fe33a31baf38b8964540d1
[clamp.git] / src / main / java / org / onap / clamp / clds / sdc / controller / installer / CsarInstallerImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2018 AT&T Intellectual Property. All rights
6  *                             reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END============================================
20  * Modifications copyright (c) 2019 Nokia
21  * ===================================================================
22  *
23  */
24
25 package org.onap.clamp.clds.sdc.controller.installer;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29 import com.google.gson.JsonObject;
30 import java.io.IOException;
31 import java.nio.charset.StandardCharsets;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 import java.util.Optional;
38 import java.util.Set;
39 import javax.annotation.PostConstruct;
40 import javax.xml.transform.TransformerException;
41 import org.apache.commons.io.IOUtils;
42 import org.json.simple.parser.ParseException;
43 import org.onap.clamp.clds.client.DcaeInventoryServices;
44 import org.onap.clamp.clds.config.sdc.BlueprintParserFilesConfiguration;
45 import org.onap.clamp.clds.config.sdc.BlueprintParserMappingConfiguration;
46 import org.onap.clamp.clds.dao.CldsDao;
47 import org.onap.clamp.clds.exception.policy.PolicyModelException;
48 import org.onap.clamp.clds.exception.sdc.controller.SdcArtifactInstallerException;
49 import org.onap.clamp.clds.model.CldsModel;
50 import org.onap.clamp.clds.model.CldsTemplate;
51 import org.onap.clamp.clds.model.dcae.DcaeInventoryResponse;
52 import org.onap.clamp.clds.model.properties.ModelProperties;
53 import org.onap.clamp.clds.service.CldsService;
54 import org.onap.clamp.clds.service.CldsTemplateService;
55 import org.onap.clamp.clds.transform.XslTransformer;
56 import org.onap.clamp.clds.util.JsonUtils;
57 import org.onap.clamp.clds.util.drawing.SvgFacade;
58 import org.springframework.beans.factory.annotation.Autowired;
59 import org.springframework.beans.factory.annotation.Value;
60 import org.springframework.context.ApplicationContext;
61 import org.springframework.stereotype.Component;
62 import org.springframework.transaction.annotation.Transactional;
63 import org.yaml.snakeyaml.Yaml;
64
65 /**
66  * This class will be instantiated by spring config, and used by Sdc Controller.
67  * There is no state kept by the bean. It's used to deploy the csar/notification
68  * received from SDC in DB.
69  */
70 @Component
71 public class CsarInstallerImpl implements CsarInstaller {
72
73     private static final EELFLogger logger = EELFManager.getInstance().getLogger(CsarInstallerImpl.class);
74     private Map<String, BlueprintParserFilesConfiguration> bpmnMapping = new HashMap<>();
75     public static final String TEMPLATE_NAME_PREFIX = "DCAE-Designer-Template-";
76     public static final String CONTROL_NAME_PREFIX = "ClosedLoop-";
77     public static final String GET_INPUT_BLUEPRINT_PARAM = "get_input";
78     // This will be used later as the policy scope
79     public static final String MODEL_NAME_PREFIX = "CLAMP";
80     /**
81      * The file name that will be loaded by Spring.
82      */
83     @Value("${clamp.config.sdc.blueprint.parser.mapping:'classpath:/clds/blueprint-parser-mapping.json'}")
84     protected String blueprintMappingFile;
85     protected ApplicationContext appContext;
86     private CldsDao cldsDao;
87     CldsTemplateService cldsTemplateService;
88     CldsService cldsService;
89     DcaeInventoryServices dcaeInventoryService;
90     private XslTransformer cldsBpmnTransformer;
91
92     @Autowired
93     public CsarInstallerImpl(ApplicationContext appContext,
94                              CldsDao cldsDao, CldsTemplateService cldsTemplateService, CldsService cldsService,
95                              DcaeInventoryServices dcaeInventoryService, XslTransformer cldsBpmnTransformer) {
96         this.appContext = appContext;
97         this.cldsDao = cldsDao;
98         this.cldsTemplateService = cldsTemplateService;
99         this.cldsService = cldsService;
100         this.dcaeInventoryService = dcaeInventoryService;
101         this.cldsBpmnTransformer = cldsBpmnTransformer;
102     }
103
104     @Autowired
105     private BlueprintParser blueprintParser;
106
107     @Autowired
108     private ChainGenerator chainGenerator;
109
110     @Autowired
111     private SvgFacade svgFacade;
112
113     @PostConstruct
114     public void loadConfiguration() throws IOException {
115         BlueprintParserMappingConfiguration
116             .createFromJson(appContext.getResource(blueprintMappingFile).getInputStream()).stream()
117             .forEach(e -> bpmnMapping.put(e.getBlueprintKey(), e.getFiles()));
118     }
119
120     @Override
121     public boolean isCsarAlreadyDeployed(CsarHandler csar) throws SdcArtifactInstallerException {
122         boolean alreadyInstalled = true;
123         for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) {
124             alreadyInstalled = alreadyInstalled
125                 && CldsModel.retrieve(cldsDao, buildModelName(csar, blueprint.getValue()), true).getId() != null;
126         }
127         return alreadyInstalled;
128     }
129
130     public static String buildModelName(CsarHandler csar, BlueprintArtifact artifact)
131         throws SdcArtifactInstallerException {
132         String policyScopePrefix = searchForPolicyScopePrefix(artifact);
133         if (policyScopePrefix.contains("*")) {
134             // This is policy_filter type
135             policyScopePrefix = policyScopePrefix.replaceAll("\\*", "");
136         } else {
137             // This is normally the get_input case
138             policyScopePrefix = MODEL_NAME_PREFIX;
139         }
140         return (policyScopePrefix + "_" + csar.getSdcCsarHelper().getServiceMetadata().getValue("name") + "_v"
141             + csar.getSdcNotification().getServiceVersion() + "_"
142             + artifact.getResourceAttached().getResourceInstanceName().replaceAll(" ", "") + "_"
143             + artifact.getBlueprintArtifactName().replace(".yaml", "")).replace('.', '_');
144     }
145
146     @Override
147     @Transactional
148     public void installTheCsar(CsarHandler csar)
149         throws SdcArtifactInstallerException, InterruptedException, PolicyModelException {
150         try {
151             logger.info("Installing the CSAR " + csar.getFilePath());
152             for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) {
153                 logger.info("Processing blueprint " + blueprint.getValue().getBlueprintArtifactName());
154                 createFakeCldsModel(csar, blueprint.getValue(),
155                     createFakeCldsTemplate(csar, blueprint.getValue(),
156                         this.searchForRightMapping(blueprint.getValue())),
157                     queryDcaeToGetServiceTypeId(blueprint.getValue()));
158             }
159             createPolicyModel(csar);
160             logger.info("Successfully installed the CSAR " + csar.getFilePath());
161         } catch (IOException e) {
162             throw new SdcArtifactInstallerException("Exception caught during the Csar installation in database", e);
163         } catch (ParseException e) {
164             throw new SdcArtifactInstallerException("Exception caught during the Dcae query to get ServiceTypeId", e);
165         }
166     }
167
168     BlueprintParserFilesConfiguration searchForRightMapping(BlueprintArtifact blueprintArtifact)
169         throws SdcArtifactInstallerException {
170         List<BlueprintParserFilesConfiguration> listConfig = new ArrayList<>();
171         Yaml yaml = new Yaml();
172         Map<String, Object> templateNodes = ((Map<String, Object>) ((Map<String, Object>) yaml
173             .load(blueprintArtifact.getDcaeBlueprint())).get("node_templates"));
174         bpmnMapping.entrySet().forEach(e -> {
175             if (templateNodes.keySet().stream().anyMatch(t -> t.contains(e.getKey()))) {
176                 listConfig.add(e.getValue());
177             }
178         });
179         if (listConfig.size() > 1) {
180             throw new SdcArtifactInstallerException(
181                 "The code does not currently support multiple MicroServices in the blueprint");
182         } else if (listConfig.isEmpty()) {
183             throw new SdcArtifactInstallerException("There is no recognized MicroService found in the blueprint");
184         }
185         logger.info("Mapping found for blueprint " + blueprintArtifact.getBlueprintArtifactName() + " is "
186             + listConfig.get(0).getBpmnXmlFilePath());
187         return listConfig.get(0);
188     }
189
190     String getAllBlueprintParametersInJson(BlueprintArtifact blueprintArtifact) {
191         JsonObject node = new JsonObject();
192         Yaml yaml = new Yaml();
193         Map<String, Object> inputsNodes = ((Map<String, Object>) ((Map<String, Object>) yaml
194             .load(blueprintArtifact.getDcaeBlueprint())).get("inputs"));
195         inputsNodes.entrySet().stream().filter(e -> !e.getKey().contains("policy_id")).forEach(elem -> {
196             Object defaultValue = ((Map<String, Object>) elem.getValue()).get("default");
197             if (defaultValue != null) {
198                 addPropertyToNode(node, elem.getKey(), defaultValue);
199             } else {
200                 node.addProperty(elem.getKey(), "");
201             }
202         });
203         node.addProperty("policy_id", "AUTO_GENERATED_POLICY_ID_AT_SUBMIT");
204         return node.toString();
205     }
206
207     private void createPolicyModel(CsarHandler csar) throws PolicyModelException {
208         try{
209             Optional<String> policyModelYaml = csar.getPolicyModelYaml();
210             // save policy model into the database
211         } catch (IOException e) {
212             throw new PolicyModelException("TransformerException when decoding the YamlText", e);
213         }
214     }
215
216     private static String searchForPolicyScopePrefix(BlueprintArtifact blueprintArtifact)
217         throws SdcArtifactInstallerException {
218         String policyName = null;
219         Yaml yaml = new Yaml();
220         List<String> policyNameList = new ArrayList<>();
221         Map<String, Object> templateNodes = ((Map<String, Object>) ((Map<String, Object>) yaml
222             .load(blueprintArtifact.getDcaeBlueprint())).get("node_templates"));
223         templateNodes.entrySet().stream().filter(e -> e.getKey().contains("policy")).forEach(ef -> {
224             String filteredPolicyName = (String) ((Map<String, Object>) ((Map<String, Object>) ef.getValue())
225                 .get("properties")).get("policy_filter");
226             if (policyName != null) {
227                 policyNameList.add(filteredPolicyName);
228             } else {
229                 String inputPolicyName = (String) ((Map<String, Object>) ((Map<String, Object>) ((Map<String, Object>) ef
230                     .getValue()).get("properties")).get("policy_id")).get(GET_INPUT_BLUEPRINT_PARAM);
231                 if (inputPolicyName != null) {
232                     policyNameList.add(GET_INPUT_BLUEPRINT_PARAM);
233                 }
234             }
235         });
236         if (policyNameList.size() > 1) {
237             throw new SdcArtifactInstallerException(
238                 "The code does not currently support multiple Policy MicroServices in the blueprint");
239         } else if (policyNameList.isEmpty()) {
240             throw new SdcArtifactInstallerException(
241                 "There is no recognized Policy MicroService found in the blueprint");
242         }
243         logger.info("policyName found in blueprint " + blueprintArtifact.getBlueprintArtifactName() + " is "
244             + policyNameList.get(0));
245         return policyNameList.get(0);
246     }
247
248     /**
249      * This call must be done when deploying the SDC notification as this call get
250      * the latest version of the artifact (version can be specified to DCAE call)
251      *
252      * @return The DcaeInventoryResponse object containing the dcae values
253      */
254     private DcaeInventoryResponse queryDcaeToGetServiceTypeId(BlueprintArtifact blueprintArtifact)
255         throws IOException, ParseException, InterruptedException {
256         return dcaeInventoryService.getDcaeInformation(blueprintArtifact.getBlueprintArtifactName(),
257             blueprintArtifact.getBlueprintInvariantServiceUuid(),
258             blueprintArtifact.getResourceAttached().getResourceInvariantUUID());
259     }
260
261     private CldsTemplate createFakeCldsTemplate(CsarHandler csar, BlueprintArtifact blueprintArtifact,
262         BlueprintParserFilesConfiguration configFiles) throws IOException, SdcArtifactInstallerException {
263
264         Set<MicroService> microServicesFromBlueprint = blueprintParser.getMicroServices(blueprintArtifact.getDcaeBlueprint()) ;
265         List<MicroService> microServicesChain = chainGenerator.getChainOfMicroServices(microServicesFromBlueprint);
266         if(microServicesChain.isEmpty()) {
267             microServicesChain = blueprintParser.fallbackToOneMicroService(blueprintArtifact.getDcaeBlueprint());
268         }
269         String imageText = svgFacade.getSvgImage(microServicesChain);
270
271         CldsTemplate template = new CldsTemplate();
272         template.setBpmnId("Sdc-Generated");
273         template.setBpmnText(IOUtils.toString(appContext.getResource(configFiles.getBpmnXmlFilePath()).getInputStream(),
274             StandardCharsets.UTF_8));
275         template.setPropText(
276             "{\"global\":[{\"name\":\"service\",\"value\":[\"" + blueprintArtifact.getDcaeBlueprint() + "\"]}]}");
277         template
278             .setImageText(imageText);
279         template.setName(TEMPLATE_NAME_PREFIX + buildModelName(csar, blueprintArtifact));
280         template.save(cldsDao, null);
281         logger.info("Fake Clds Template created for blueprint " + blueprintArtifact.getBlueprintArtifactName()
282             + " with name " + template.getName());
283         return template;
284     }
285
286     private CldsModel createFakeCldsModel(CsarHandler csar, BlueprintArtifact blueprintArtifact,
287         CldsTemplate cldsTemplate, DcaeInventoryResponse dcaeInventoryResponse) throws SdcArtifactInstallerException {
288
289         if (dcaeInventoryResponse == null) {
290             throw new SdcArtifactInstallerException(
291                 "DCAE inventory response is NULL, query to DCAE fail to be answered properly, this is required to deploy CSAR properly !!!");
292         }
293         try {
294             CldsModel cldsModel = new CldsModel();
295             cldsModel.setName(buildModelName(csar, blueprintArtifact));
296             cldsModel.setBlueprintText(blueprintArtifact.getDcaeBlueprint());
297             cldsModel.setTemplateName(cldsTemplate.getName());
298             cldsModel.setTemplateId(cldsTemplate.getId());
299             cldsModel.setBpmnText(cldsTemplate.getBpmnText());
300             cldsModel.setTypeId(dcaeInventoryResponse.getTypeId());
301             cldsModel.setTypeName(dcaeInventoryResponse.getTypeName());
302             cldsModel.setControlNamePrefix(CONTROL_NAME_PREFIX);
303             // We must save it otherwise object won't be created in db
304             // and proptext will always be null
305             cldsModel.setPropText("{\"global\":[]}");
306             // Must save first to have the generated id available to generate
307             // the policyId
308             cldsModel = cldsModel.save(cldsDao, null);
309             cldsModel = setModelPropText(cldsModel, blueprintArtifact, cldsTemplate);
310             logger.info("Fake Clds Model created for blueprint " + blueprintArtifact.getBlueprintArtifactName()
311                 + " with name " + cldsModel.getName());
312             return cldsModel;
313         } catch (TransformerException e) {
314             throw new SdcArtifactInstallerException("TransformerException when decoding the BpmnText", e);
315         }
316     }
317
318     private CldsModel setModelPropText(CldsModel cldsModel, BlueprintArtifact blueprintArtifact,
319         CldsTemplate cldsTemplate) throws TransformerException {
320         // Do a test to validate the BPMN
321         new ModelProperties(cldsModel.getName(), cldsModel.getControlName(), "PUT", false,
322             cldsBpmnTransformer.doXslTransformToString(cldsTemplate.getBpmnText()), "{}");
323         String inputParams = "{\"name\":\"deployParameters\",\"value\":"
324             + getAllBlueprintParametersInJson(blueprintArtifact) + "}";
325         cldsModel.setPropText("{\"global\":[{\"name\":\"service\",\"value\":[\""
326             + blueprintArtifact.getBlueprintInvariantServiceUuid() + "\"]},{\"name\":\"vf\",\"value\":[\""
327             + blueprintArtifact.getResourceAttached().getResourceInvariantUUID()
328             + "\"]},{\"name\":\"actionSet\",\"value\":[\"vnfRecipe\"]},{\"name\":\"location\",\"value\":[\"DC1\"]},"
329             + inputParams + "]}");
330         return cldsModel.save(cldsDao, null);
331     }
332
333     private void addPropertyToNode(JsonObject node, String key, Object value) {
334         if (value instanceof String) {
335             node.addProperty(key, (String) value);
336         } else if (value instanceof Number) {
337             node.addProperty(key, (Number) value);
338         } else if (value instanceof Boolean) {
339             node.addProperty(key, (Boolean) value);
340         } else if (value instanceof Character) {
341             node.addProperty(key, (Character) value);
342         } else {
343             node.addProperty(key, JsonUtils.GSON.toJson(value));
344         }
345     }
346 }