Create 1.13.0 maven release
[aai/babel.git] / src / main / java / org / onap / aai / babel / xml / generator / api / AaiArtifactGenerator.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright (c) 2017-2019 AT&T Intellectual Property. All rights reserved.
6  * Copyright (c) 2017-2019 European Software Marketing Ltd.
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  */
21
22 package org.onap.aai.babel.xml.generator.api;
23
24 import java.io.IOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.stream.Collectors;
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.lang3.StringUtils;
36 import org.onap.aai.babel.logging.ApplicationMsgs;
37 import org.onap.aai.babel.logging.LogHelper;
38 import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser;
39 import org.onap.aai.babel.parser.ToscaParser;
40 import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException;
41 import org.onap.aai.babel.xml.generator.data.AdditionalParams;
42 import org.onap.aai.babel.xml.generator.data.Artifact;
43 import org.onap.aai.babel.xml.generator.data.ArtifactType;
44 import org.onap.aai.babel.xml.generator.data.GenerationData;
45 import org.onap.aai.babel.xml.generator.data.GeneratorUtil;
46 import org.onap.aai.babel.xml.generator.data.GroupType;
47 import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil;
48 import org.onap.aai.babel.xml.generator.model.Model;
49 import org.onap.aai.babel.xml.generator.model.Resource;
50 import org.onap.aai.babel.xml.generator.model.Service;
51 import org.onap.aai.babel.xml.generator.model.Widget;
52 import org.onap.aai.babel.xml.generator.model.WidgetType;
53 import org.onap.aai.babel.xml.generator.types.ModelType;
54 import org.onap.aai.cl.api.Logger;
55 import org.onap.sdc.tosca.parser.api.IEntityDetails;
56 import org.onap.sdc.tosca.parser.api.ISdcCsarHelper;
57 import org.onap.sdc.tosca.parser.elements.queries.EntityQuery;
58 import org.onap.sdc.tosca.parser.elements.queries.TopologyTemplateQuery;
59 import org.onap.sdc.tosca.parser.enums.SdcTypes;
60 import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException;
61 import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory;
62 import org.onap.sdc.toscaparser.api.Group;
63 import org.onap.sdc.toscaparser.api.NodeTemplate;
64 import org.onap.sdc.toscaparser.api.Property;
65 import org.onap.sdc.toscaparser.api.elements.Metadata;
66 import org.slf4j.MDC;
67
68 public class AaiArtifactGenerator implements ArtifactGenerator {
69
70     private static final String SDNC_MODEL_VERSION = "sdnc_model_version";
71     private static final String SDNC_MODEL_NAME = "sdnc_model_name";
72     private static Logger log = LogHelper.INSTANCE;
73
74     private static final String MDC_PARAM_MODEL_INFO = "ARTIFACT_MODEL_INFO";
75     private static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml";
76     private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA =
77             "Service tosca missing from list of input artifacts";
78     private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION =
79             "Cannot generate artifacts. Service version is not specified";
80     private static final String GENERATOR_AAI_INVALID_SERVICE_VERSION =
81             "Cannot generate artifacts. Service version is incorrect";
82
83     private AaiModelGenerator modelGenerator = new AaiModelGenerator();
84
85     @Override
86     public GenerationData generateArtifact(byte[] csarArchive, List<Artifact> input,
87             Map<String, String> additionalParams) {
88         String configLocation = System.getProperty(ArtifactGeneratorToscaParser.PROPERTY_TOSCA_MAPPING_FILE);
89         if (configLocation == null) {
90             throw new IllegalArgumentException(
91                     String.format(ArtifactGeneratorToscaParser.GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND,
92                             ArtifactGeneratorToscaParser.PROPERTY_TOSCA_MAPPING_FILE));
93         }
94
95         try {
96             ArtifactGeneratorToscaParser.initToscaMappingsConfiguration(configLocation);
97         } catch (IOException e) {
98             log.error(ApplicationMsgs.LOAD_PROPERTIES, e, configLocation);
99             return createErrorData(e);
100         }
101
102         Path csarPath;
103
104         try {
105             csarPath = createTempFile(csarArchive);
106         } catch (IOException e) {
107             log.error(ApplicationMsgs.TEMP_FILE_ERROR, e);
108             return createErrorData(e);
109         }
110
111         try {
112             ISdcCsarHelper csarHelper =
113                     SdcToscaParserFactory.getInstance().getSdcCsarHelper(csarPath.toAbsolutePath().toString());
114             return generateAllArtifacts(validateServiceVersion(additionalParams), csarHelper);
115         } catch (SdcToscaParserException | ClassCastException | XmlArtifactGenerationException e) {
116             log.error(ApplicationMsgs.INVALID_CSAR_FILE, e);
117             return createErrorData(e);
118         } finally {
119             FileUtils.deleteQuietly(csarPath.toFile());
120         }
121     }
122
123     private GenerationData createErrorData(Exception e) {
124         GenerationData generationData = new GenerationData();
125         generationData.add(ArtifactType.AAI.name(), e.getMessage());
126         return generationData;
127     }
128
129     /**
130      * Generate model artifacts for the Service and its associated Resources.
131      *
132      * @param serviceVersion
133      * @param csarHelper
134      *            interface to the TOSCA parser
135      * @return the generated Artifacts (containing XML models)
136      * @throws XmlArtifactGenerationException
137      *             if the configured widget mappings do not support processed widget type(s)
138      */
139     public GenerationData generateAllArtifacts(final String serviceVersion, ISdcCsarHelper csarHelper)
140             throws XmlArtifactGenerationException {
141         Service serviceModel = createServiceModel(serviceVersion, csarHelper.getServiceMetadataAllProperties());
142
143         MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel));
144
145         List<Resource> resources = generateResourceModels(csarHelper, serviceModel);
146
147         // Generate the A&AI XML model for the Service.
148         final String serviceArtifact = modelGenerator.generateModelFor(serviceModel);
149
150         // Build a Babel Artifact to be returned to the caller.
151         GenerationData generationData = new GenerationData();
152         generationData.add(getServiceArtifact(serviceModel, serviceArtifact));
153
154         // For each Resource, generate the A&AI XML model and then create an additional Artifact for that model.
155         for (Resource resource : resources) {
156             generateResourceArtifact(generationData, resource);
157             for (Resource childResource : resource.getResources()) {
158                 boolean isProvidingService =
159                         (boolean) Optional.ofNullable(childResource.getProperties().get("providingService")) //
160                                 .orElse(false);
161                 if (!isProvidingService) {
162                     generateResourceArtifact(generationData, childResource);
163                 }
164             }
165         }
166
167         return generationData;
168     }
169
170     /**
171      * Create a Service from the provided metadata
172      *
173      * @param serviceVersion
174      * @param properties
175      * @return
176      */
177     private Service createServiceModel(final String serviceVersion, Map<String, String> properties) {
178         log.debug("Processing (TOSCA) Service object");
179         Service serviceModel = new Service();
180         serviceModel.setModelVersion(serviceVersion);
181         serviceModel.populateModelIdentificationInformation(properties);
182         return serviceModel;
183     }
184
185     /**
186      * @param csarHelper
187      * @param serviceModel
188      * @return the generated Models
189      * @throws XmlArtifactGenerationException
190      *             if the configured widget mappings do not support processed widget type(s)
191      */
192     private List<Resource> generateResourceModels(ISdcCsarHelper csarHelper, Service serviceModel)
193             throws XmlArtifactGenerationException {
194         List<NodeTemplate> serviceNodeTemplates =
195                 ToscaParser.getServiceNodeTemplates(csarHelper).collect(Collectors.toList());
196         if (serviceNodeTemplates == null) {
197             throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA);
198         }
199
200         final ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper);
201         List<Resource> resources = new ArrayList<>();
202         final List<Group> serviceGroups = ToscaParser.getServiceLevelGroups(csarHelper);
203         for (NodeTemplate nodeTemplate : serviceNodeTemplates) {
204             if (nodeTemplate.getMetaData() != null) {
205                 generateModelFromNodeTemplate(csarHelper, serviceModel, resources, serviceGroups, parser, nodeTemplate);
206             } else {
207                 log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName());
208             }
209         }
210
211         return resources;
212     }
213
214     /**
215      * @param csarHelper
216      * @param serviceModel
217      * @param resources
218      * @param serviceGroups
219      * @param parser
220      * @param nodeTemplate
221      * @throws XmlArtifactGenerationException
222      *             if the configured widget mappings do not support processed widget type(s)
223      */
224     private void generateModelFromNodeTemplate(ISdcCsarHelper csarHelper, Service serviceModel,
225             List<Resource> resources, final List<Group> serviceGroups, ArtifactGeneratorToscaParser parser,
226             NodeTemplate nodeTemplate) throws XmlArtifactGenerationException {
227         Resource model = getModelFor(parser, nodeTemplate);
228
229         if (model != null) {
230             if (nodeTemplate.getMetaData() != null) {
231                 model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
232             }
233
234             parser.addRelatedModel(serviceModel, model);
235             if (model.getModelType() == ModelType.RESOURCE) {
236                 generateResourceModel(csarHelper, resources, parser, nodeTemplate);
237             }
238         } else {
239             for (Group group : serviceGroups) {
240                 ArrayList<String> members = group.getMembers();
241                 if (members != null && members.contains(nodeTemplate.getName())
242                         && WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) {
243                     log.debug(String.format("Adding group %s (type %s) with members %s", group.getName(),
244                             group.getType(), members));
245
246                     Resource groupModel = parser.createInstanceGroupModel(
247                             parser.mergeProperties(group.getMetadata().getAllProperties(), group.getProperties()));
248                     serviceModel.addResource(groupModel);
249                     resources.add(groupModel);
250                 }
251             }
252         }
253     }
254
255     private Resource getModelFor(ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) {
256         String nodeTypeName = nodeTemplate.getType();
257
258         log.debug("Processing resource " + nodeTypeName + ": " + nodeTemplate.getMetaData().getValue("UUID"));
259
260         Resource model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
261
262         if (model != null) {
263             Metadata metadata = nodeTemplate.getMetaData();
264             if (metadata != null && parser.hasAllottedResource(metadata.getAllProperties())
265                     && model.hasWidgetType("VF")) {
266                 model = new Resource(WidgetType.valueOf("ALLOTTED_RESOURCE"), true);
267             }
268         }
269
270         return model;
271     }
272
273     /**
274      * @param csarHelper
275      * @param resources
276      * @param parser
277      * @param serviceVfNode
278      *            a VF resource Node Template
279      * @throws XmlArtifactGenerationException
280      *             if the configured widget mappings do not support processed widget type(s)
281      */
282     private void generateResourceModel(ISdcCsarHelper csarHelper, List<Resource> resources,
283             ArtifactGeneratorToscaParser parser, NodeTemplate serviceVfNode) throws XmlArtifactGenerationException {
284         Resource resourceModel = getModelFor(parser, serviceVfNode);
285         if (resourceModel == null) {
286             log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Could not generate resource model");
287             return;
288         }
289
290         Map<String, String> serviceMetadata = serviceVfNode.getMetaData().getAllProperties();
291         resourceModel.populateModelIdentificationInformation(serviceMetadata);
292
293         Map<String, String> pnfProps = getResourceProperties(csarHelper, SdcTypes.PNF);
294         resourceModel.populateModelIdentificationInformation(pnfProps);
295
296         Map<String, String> vfProps = getResourceProperties(csarHelper, SdcTypes.VF);
297         resourceModel.populateModelIdentificationInformation(vfProps);
298
299         parser.processResourceModels(resourceModel, getNonVnfChildren(serviceVfNode));
300
301         List<NodeTemplate> serviceVfList = ToscaParser.getServiceNodeTemplates(csarHelper)
302                 .filter(ToscaParser.filterOnType(SdcTypes.VF)).collect(Collectors.toList());
303
304         if (serviceVfList != null) {
305             parser.processVfModules(resources, resourceModel, serviceVfNode);
306         }
307
308         if (parser.hasSubCategoryTunnelXConnect(serviceMetadata) && parser.hasAllottedResource(serviceMetadata)) {
309             resourceModel.addWidget(Widget.createWidget("TUNNEL_XCONNECT"));
310         }
311
312         resources.addAll(parser.processInstanceGroups(resourceModel, serviceVfNode));
313         resources.add(resourceModel);
314     }
315
316     private Map<String, String> getResourceProperties(ISdcCsarHelper csarHelper, SdcTypes type) {
317         EntityQuery entityQuery = EntityQuery.newBuilder(type).build();
318         TopologyTemplateQuery topologyTemplateQuery = TopologyTemplateQuery.newBuilder(SdcTypes.SERVICE).build();
319         List<IEntityDetails> entityDetailsList = csarHelper.getEntity(entityQuery, topologyTemplateQuery, false);
320         Map<String, String> props = new HashMap<>();
321         for (IEntityDetails entityDetails : entityDetailsList) {
322             Map<String, Property> properties = entityDetails.getProperties();
323             if (properties.get(SDNC_MODEL_VERSION) != null && properties.get(SDNC_MODEL_NAME) != null) {
324                 props.put(SDNC_MODEL_VERSION, String.valueOf(properties.get(SDNC_MODEL_VERSION).getValue()));
325                 props.put(SDNC_MODEL_NAME, String.valueOf(properties.get(SDNC_MODEL_NAME).getValue()));
326             }
327         }
328         return props;
329     }
330
331     /**
332      * Return all child Node Templates (via Substitution Mappings) that do not have a type ending VnfConfiguration.
333      *
334      * @param nodeTemplate
335      *            the parent Node Template
336      * @return the child Node Templates which are not a VNF Configuration type
337      */
338     private List<NodeTemplate> getNonVnfChildren(NodeTemplate nodeTemplate) {
339         return Optional.ofNullable(nodeTemplate.getSubMappingToscaTemplate()) //
340                 .map(sm -> Optional.ofNullable(sm.getNodeTemplates())
341                         .map(nts -> nts.stream().filter(nt -> !isVNFType(nt)) //
342                                 .collect(Collectors.toList()))
343                         .orElse(Collections.emptyList()))
344                 .orElse(Collections.emptyList());
345     }
346
347     private boolean isVNFType(NodeTemplate nt) {
348         return nt.getType().endsWith("VnfConfiguration");
349     }
350
351     /**
352      * @param generationData
353      * @param resource
354      * @throws XmlArtifactGenerationException
355      */
356     private void generateResourceArtifact(GenerationData generationData, Resource resource)
357             throws XmlArtifactGenerationException {
358         if (!isContained(generationData, getArtifactName(resource))) {
359             log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating resource model");
360             generationData.add(getResourceArtifact(resource, modelGenerator.generateModelFor(resource)));
361         }
362     }
363
364     private Path createTempFile(byte[] bytes) throws IOException {
365         log.debug("Creating temp file on file system for the csar");
366         Path path = Files.createTempFile("temp", ".csar");
367         Files.write(path, bytes);
368         return path;
369     }
370
371     /**
372      * Create the artifact label for an AAI model.
373      *
374      * @param model
375      * @return the artifact label as String
376      */
377     private String getArtifactLabel(Model model) {
378         StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name());
379         artifactName.append("-");
380         artifactName.append(model.getModelTypeName());
381         artifactName.append("-");
382         artifactName.append(hashCodeUuId(model.getModelNameVersionId()));
383         return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-");
384     }
385
386     /**
387      * Method to generate the artifact name for an AAI model.
388      *
389      * @param model
390      *            AAI artifact model
391      * @return Model artifact name
392      */
393     private String getArtifactName(Model model) {
394         StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name());
395         artifactName.append("-");
396
397         String truncatedArtifactName = truncateName(model.getModelName());
398         artifactName.append(truncatedArtifactName);
399
400         artifactName.append("-");
401         artifactName.append(model.getModelTypeName());
402         artifactName.append("-");
403         artifactName.append(model.getModelVersion());
404
405         artifactName.append(".");
406         artifactName.append(GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION);
407         return artifactName.toString();
408     }
409
410     /**
411      * Create Resource artifact model from the AAI xml model string.
412      *
413      * @param resourceModel
414      *            Model of the resource artifact
415      * @param aaiResourceModel
416      *            AAI model as string
417      * @return Generated {@link Artifact} model for the resource
418      */
419     private Artifact getResourceArtifact(Resource resourceModel, String aaiResourceModel) {
420         final String resourceArtifactLabel = getArtifactLabel(resourceModel);
421         MDC.put(MDC_PARAM_MODEL_INFO, resourceModel.getModelName() + "," + resourceArtifactLabel);
422         final byte[] bytes = aaiResourceModel.getBytes();
423
424         Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(),
425                 GeneratorUtil.checkSum(bytes), GeneratorUtil.encode(bytes));
426         artifact.setName(getArtifactName(resourceModel));
427         artifact.setLabel(resourceArtifactLabel);
428         artifact.setDescription("AAI Resource Model");
429         return artifact;
430     }
431
432     /**
433      * @param generationData
434      * @param artifactName
435      * @return
436      */
437     private boolean isContained(GenerationData generationData, final String artifactName) {
438         return generationData.getResultData().stream()
439                 .anyMatch(artifact -> StringUtils.equals(artifact.getName(), artifactName));
440     }
441
442     /**
443      * Create Service artifact model from the AAI XML model.
444      *
445      * @param serviceModel
446      *            Model of the service artifact
447      * @param aaiServiceModel
448      *            AAI model as string
449      * @return Generated {@link Artifact} model for the service
450      */
451     private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) {
452         Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(),
453                 GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes()));
454         String serviceArtifactName = getArtifactName(serviceModel);
455         String serviceArtifactLabel = getArtifactLabel(serviceModel);
456         artifact.setName(serviceArtifactName);
457         artifact.setLabel(serviceArtifactLabel);
458         artifact.setDescription("AAI Service Model");
459         return artifact;
460     }
461
462     private int hashCodeUuId(String uuId) {
463         int hashcode = 0;
464         if (uuId != null) {
465             for (int i = 0; i < uuId.length(); i++) {
466                 hashcode = 31 * hashcode + uuId.charAt(i);
467             }
468         }
469         return hashcode;
470     }
471
472     private String truncateName(String name) {
473         String truncatedName = name;
474         if (name != null && name.length() >= 200) {
475             truncatedName = name.substring(0, 199);
476         }
477         return truncatedName;
478     }
479
480     private String validateServiceVersion(Map<String, String> additionalParams) {
481         String serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName());
482         if (serviceVersion == null) {
483             throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION);
484         } else {
485             String versionRegex = "^\\d*\\.\\d*$";
486             if (!(serviceVersion.matches(versionRegex))) {
487                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_INVALID_SERVICE_VERSION));
488             }
489         }
490         return serviceVersion;
491     }
492 }