ed7fc196acf608250abdd40d2682d3a8eb5c3192
[aai/babel.git] / src / main / java / org / onap / aai / babel / parser / ArtifactGeneratorToscaParser.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 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.parser;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Properties;
34 import java.util.stream.Collectors;
35 import java.util.stream.Stream;
36 import org.onap.aai.babel.logging.ApplicationMsgs;
37 import org.onap.aai.babel.logging.LogHelper;
38 import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil;
39 import org.onap.aai.babel.xml.generator.model.AllotedResource;
40 import org.onap.aai.babel.xml.generator.model.InstanceGroup;
41 import org.onap.aai.babel.xml.generator.model.L3NetworkWidget;
42 import org.onap.aai.babel.xml.generator.model.Model;
43 import org.onap.aai.babel.xml.generator.model.ProvidingService;
44 import org.onap.aai.babel.xml.generator.model.Resource;
45 import org.onap.aai.babel.xml.generator.model.Service;
46 import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget;
47 import org.onap.aai.babel.xml.generator.model.VfModule;
48 import org.onap.aai.babel.xml.generator.model.Widget;
49 import org.onap.aai.babel.xml.generator.types.ModelType;
50 import org.onap.aai.cl.api.Logger;
51 import org.onap.sdc.tosca.parser.api.ISdcCsarHelper;
52 import org.onap.sdc.toscaparser.api.Group;
53 import org.onap.sdc.toscaparser.api.NodeTemplate;
54 import org.onap.sdc.toscaparser.api.Property;
55 import org.onap.sdc.toscaparser.api.elements.Metadata;
56
57 public class ArtifactGeneratorToscaParser {
58
59         private static Logger log = LogHelper.INSTANCE;
60
61         public static final String PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE = "artifactgenerator.config";
62         public static final String PROPERTY_GROUP_FILTERS_CONFIG_FILE = "groupfilter.config";
63
64         private static final String GENERATOR_AAI_CONFIGFILE_NOT_FOUND = "Cannot generate artifacts. Artifact Generator Configuration file not found at %s";
65         private static final String GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND = "Cannot generate artifacts. System property %s not configured";
66         private static final String GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING = "Cannot generate artifacts. Providing Service Metadata is missing for allotted resource %s";
67         private static final String GENERATOR_AAI_PROVIDING_SERVICE_MISSING = "Cannot generate artifacts. Providing Service is missing for allotted resource %s";
68
69         // Metadata properties
70         private static final String CATEGORY = "category";
71         private static final String ALLOTTED_RESOURCE = "Allotted Resource";
72         private static final String SUBCATEGORY = "subcategory";
73         private static final String TUNNEL_XCONNECT = "Tunnel XConnect";
74
75         private static final String VERSION = "version";
76
77         private ISdcCsarHelper csarHelper;
78
79         /**
80          * Constructs using csarHelper
81          *
82          * @param csarHelper
83          *            The csar helper
84          */
85         public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) {
86                 this.csarHelper = csarHelper;
87         }
88
89         /**
90          * Returns the artifact description
91          *
92          * @param model
93          *            the artifact model
94          * @return the artifact model's description
95          */
96         public static String getArtifactDescription(Model model) {
97                 String artifactDesc = model.getModelDescription();
98                 if (model.getModelType().equals(ModelType.SERVICE)) {
99                         artifactDesc = "AAI Service Model";
100                 } else if (model.getModelType().equals(ModelType.RESOURCE)) {
101                         artifactDesc = "AAI Resource Model";
102                 }
103                 return artifactDesc;
104         }
105
106         /**
107          * Initialises the widget configuration.
108          *
109          * @throws IOException
110          */
111         public static void initWidgetConfiguration() throws IOException {
112                 log.debug("Getting Widget Configuration");
113                 String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE);
114                 if (configLocation != null) {
115                         File file = new File(configLocation);
116                         if (file.exists()) {
117                                 Properties properties = new Properties();
118                                 properties.load(new FileInputStream(file));
119                                 WidgetConfigurationUtil.setConfig(properties);
120                         } else {
121                                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
122                         }
123                 } else {
124                         throw new IllegalArgumentException(
125                                         String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE));
126                 }
127         }
128
129         /**
130          * Initialises the group filter configuration.
131          *
132          * @throws IOException
133          */
134         public static void initGroupFilterConfiguration() throws IOException {
135                 log.debug("Getting Filter Tyoes Configuration");
136                 String configLocation = System.getProperty(PROPERTY_GROUP_FILTERS_CONFIG_FILE);
137                 if (configLocation != null) {
138                         File file = new File(configLocation);
139                         if (file.exists()) {
140                                 Properties properties = new Properties();
141                                 properties.load(new FileInputStream(file));
142                                 WidgetConfigurationUtil.setFilterConfig(properties);
143                         } else {
144                                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
145                         }
146                 } else {
147                         throw new IllegalArgumentException(
148                                         String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_GROUP_FILTERS_CONFIG_FILE));
149                 }
150         }
151
152         /**
153          * Process the service TOSCA.
154          *
155          * @param service
156          *            model of the service artifact
157          * @param idTypeStore
158          *            ID->Type mapping
159          * @param nodeTemplates
160          *            a list of service nodes
161          */
162         public void processServiceTosca(Service service, Map<String, String> idTypeStore,
163                         List<NodeTemplate> nodeTemplates) {
164                 log.debug("Processing (TOSCA) Service object");
165
166                 for (NodeTemplate nodeTemplate : nodeTemplates) {
167                         if (nodeTemplate.getMetaData() != null) {
168                                 addNodeToService(idTypeStore, service, nodeTemplate);
169                         } else {
170                                 log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName());
171                         }
172                 }
173         }
174
175         /**
176          * Generates a Resource List using input Service Node Templates.
177          *
178          * @param serviceNodeTemplates
179          *            input Service Node Templates
180          * @param idTypeStore
181          *            ID->Type mapping
182          *
183          * @return the processed resource models
184          */
185         public List<Resource> processResourceToscas(List<NodeTemplate> serviceNodeTemplates,
186                         Map<String, String> idTypeStore) {
187                 List<Resource> resources = new LinkedList<>();
188                 for (NodeTemplate serviceNodeTemplate : serviceNodeTemplates) {
189                         if (serviceNodeTemplate.getMetaData() != null) {
190                                 resources.addAll(processResourceTosca(idTypeStore, serviceNodeTemplate,
191                                                 csarHelper.getNodeTemplateChildren(serviceNodeTemplate)));
192                         } else {
193                                 log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, serviceNodeTemplate.getName());
194                         }
195                 }
196                 return resources;
197         }
198
199         /**
200          * @param idTypeStore
201          *            ID->Type mapping
202          * @param serviceNodeTemplate
203          * @param resourceNodeTemplates
204          *            the (non-VNF) substituted node templates
205          * @return the processed resource models
206          */
207         private List<Resource> processResourceTosca(Map<String, String> idTypeStore, NodeTemplate serviceNodeTemplate,
208                         List<NodeTemplate> resourceNodeTemplates) {
209                 List<Resource> resources = new LinkedList<>();
210                 String resourceUuId = serviceNodeTemplate.getMetaData().getValue("UUID");
211                 String nodeTypeName = idTypeStore.get(resourceUuId);
212                 if (nodeTypeName != null) {
213                         Model resourceModel = Model.getModelFor(nodeTypeName, serviceNodeTemplate.getMetaData().getValue("type"));
214
215                         log.debug("Processing resource " + nodeTypeName + ": " + resourceUuId);
216                         Map<String, String> serviceMetadata = serviceNodeTemplate.getMetaData().getAllProperties();
217                         resourceModel.populateModelIdentificationInformation(serviceMetadata);
218
219                         idTypeStore.remove(resourceModel.getModelNameVersionId());
220                         processResourceModels(idTypeStore, resourceModel, resourceNodeTemplates);
221
222                         if (csarHelper.getServiceVfList() != null) {
223                                 processVfModules(resources, resourceModel, serviceNodeTemplate);
224                         }
225
226                         if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) {
227                                 resourceModel.addWidget(new TunnelXconnectWidget());
228                         }
229
230                         resources.addAll(processInstanceGroups(resourceModel, serviceNodeTemplate));
231                         resources.add((Resource) resourceModel);
232                 }
233
234                 return resources;
235         }
236
237         /**
238          * Process groups for this service node, according to the defined filter.
239          *
240          * @param resourceModel
241          * @param serviceNode
242          * @return resources for which XML Models should be generated
243          */
244         List<Resource> processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) {
245                 List<Resource> resources = new ArrayList<>();
246                 if (serviceNode.getSubMappingToscaTemplate() != null) {
247                         List<Group> serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode);
248                         for (Group group : serviceGroups) {
249                                 if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) {
250                                         resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(),
251                                                         group.getMetadata().getAllProperties(), group.getProperties()));
252                                 }
253                         }
254                 }
255                 return resources;
256         }
257
258         /**
259          * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are
260          * duplicate keys then the TOSCA Property value takes precedence.
261          *
262          * @param stringProps
263          *            initial Map of String property values (e.g. from the TOSCA YAML metadata section)
264          * @param toscaProps
265          *            Map of TOSCA Property Type Object values to merge in (or overwrite)
266          * @return a Map of the property values converted to String
267          */
268         private Map<String, String> mergeProperties(Map<String, String> stringProps, Map<String, Property> toscaProps) {
269                 Map<String, String> props = new HashMap<>(stringProps);
270                 toscaProps.forEach((key, toscaProp) -> props.put(key,
271                                 toscaProp.getValue() == null ? "" : toscaProp.getValue().toString()));
272                 return props;
273         }
274
275         /**
276          * Create an Instance Group Model and populate it with the supplied data.
277          *
278          * @param resourceModel
279          *            the Resource node template Model
280          * @param memberNodes
281          *            the Resources and Widgets belonging to the Group
282          * @param metaProperties
283          *            the metadata of the Group
284          * @param properties
285          *            the properties of the Group
286          * @return the Instance Group and Member resource models
287          */
288         private List<Resource> processInstanceGroup(Model resourceModel, ArrayList<NodeTemplate> memberNodes,
289                         Map<String, String> metaProperties, Map<String, Property> properties) {
290                 Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties));
291                 resourceModel.addResource(groupModel);
292                 List<Resource> resources = Stream.of(groupModel).collect(Collectors.toList());
293
294                 if (memberNodes != null && !memberNodes.isEmpty()) {
295                         resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel));
296                 }
297
298                 return resources;
299         }
300
301         private Resource createInstanceGroupModel(Map<String, String> properties) {
302                 Resource groupModel = new InstanceGroup();
303                 groupModel.populateModelIdentificationInformation(properties);
304                 return groupModel;
305         }
306
307         /**
308          * @param memberNodes
309          * @param groupModel
310          * @return
311          */
312         private List<Resource> generateResourcesAndWidgets(final ArrayList<NodeTemplate> memberNodes,
313                         final Resource groupModel) {
314                 List<Resource> resources = new ArrayList<>();
315                 for (NodeTemplate nodeTemplate : memberNodes) {
316                         String nodeTypeName = normaliseNodeTypeName(nodeTemplate);
317                         Model memberModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
318                         memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
319
320                         log.debug(String.format("Generating grouped %s (%s) from TOSCA type %s",
321                                         memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName));
322
323                         addRelatedModel(groupModel, memberModel);
324                         if (memberModel instanceof Resource) {
325                                 resources.add((Resource) memberModel);
326                         }
327                 }
328                 return resources;
329         }
330
331         /**
332          * Add the supplied Node Template to the Service, provided that it is a valid Resource or Widget. If the Node
333          * Template is a Resource type, this is also recorded in the supplied nodesById Map.
334          *
335          * @param nodesById
336          *            a map of Resource node type names, keyed by UUID
337          * @param service
338          *            the Service to which the Node Template should be added
339          * @param nodeTemplate
340          *            the Node Template to add (only if this is a Resource or Widget type)
341          */
342         private void addNodeToService(Map<String, String> nodesById, Service service, NodeTemplate nodeTemplate) {
343                 String nodeTypeName = normaliseNodeTypeName(nodeTemplate);
344                 Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
345                 if (model != null) {
346                         if (nodeTemplate.getMetaData() != null) {
347                                 model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
348                         }
349
350                         addRelatedModel(service, model);
351                         if (model instanceof Resource) {
352                                 nodesById.put(model.getModelNameVersionId(), nodeTypeName);
353                         }
354                 }
355         }
356
357         /**
358          * @param model
359          * @param relation
360          */
361         private void addRelatedModel(final Model model, final Model relation) {
362                 if (relation instanceof Resource) {
363                         model.addResource((Resource) relation);
364                 } else {
365                         model.addWidget((Widget) relation);
366                 }
367         }
368
369         /**
370          * Process TOSCA Group information for VF Modules.
371          *
372          * @param resources
373          * @param model
374          * @param serviceNode
375          */
376         private void processVfModules(List<Resource> resources, Model resourceModel, NodeTemplate serviceNode) {
377                 // Get the customisation UUID for each VF node and use it to get its Groups
378                 String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNode);
379                 List<Group> serviceGroups = csarHelper.getVfModulesByVf(uuid);
380
381                 // Process each VF Group
382                 for (Group serviceGroup : serviceGroups) {
383                         Model groupModel = Model.getModelFor(serviceGroup.getType());
384                         if (groupModel instanceof VfModule) {
385                                 processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel);
386                         }
387                 }
388         }
389
390         private void processVfModule(List<Resource> resources, Model model, Group groupDefinition, NodeTemplate serviceNode,
391                         VfModule groupModel) {
392                 groupModel.populateModelIdentificationInformation(
393                                 mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties()));
394                 processVfModuleGroup(resources, model, groupDefinition, serviceNode, groupModel);
395         }
396
397         private void processVfModuleGroup(List<Resource> resources, Model model, Group groupDefinition,
398                         NodeTemplate serviceNode, VfModule groupModel) {
399                 // Get names of the members of the service group
400                 List<NodeTemplate> members = csarHelper.getMembersOfVfModule(serviceNode, groupDefinition);
401                 if (members != null && !members.isEmpty()) {
402                         List<String> memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList());
403                         groupModel.setMembers(memberNames);
404                         for (NodeTemplate member : members) {
405                                 processGroupMembers(groupModel, member);
406                         }
407                 }
408
409                 model.addResource(groupModel); // Added group (VfModule) to the (VF) model
410                 // Check if we have already encountered the same VfModule across all the artifacts
411                 if (!resources.contains(groupModel)) {
412                         resources.add(groupModel);
413                 }
414         }
415
416         private void processGroupMembers(Model group, NodeTemplate member) {
417                 Model resourceNode;
418                 // L3-network inside vf-module to be generated as Widget a special handling.
419                 if (member.getType().contains("org.openecomp.resource.vl")) {
420                         resourceNode = new L3NetworkWidget();
421                 } else {
422                         resourceNode = Model.getModelFor(member.getType());
423                 }
424                 if (resourceNode != null && !(resourceNode instanceof Resource)) {
425                         Widget widget = (Widget) resourceNode;
426                         widget.addKey(member.getName());
427                         // Add the widget element encountered to the Group model
428                         group.addWidget(widget);
429                 }
430         }
431
432         private String normaliseNodeTypeName(NodeTemplate nodeType) {
433                 String nodeTypeName = nodeType.getType();
434                 Metadata metadata = nodeType.getMetaData();
435                 if (metadata != null && hasAllottedResource(metadata.getAllProperties())) {
436                         if (nodeType.getType().contains("org.openecomp.resource.vf.")) {
437                                 nodeTypeName = "org.openecomp.resource.vf.allottedResource";
438                         }
439                         if (nodeType.getType().contains("org.openecomp.resource.vfc.")) {
440                                 nodeTypeName = "org.openecomp.resource.vfc.AllottedResource";
441                         }
442                 }
443                 return nodeTypeName;
444         }
445
446         private boolean hasAllottedResource(Map<String, String> metadata) {
447                 return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY));
448         }
449
450         private boolean hasSubCategoryTunnelXConnect(Map<String, String> metadata) {
451                 return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY));
452         }
453
454         /**
455          * Create a Map of property name against String property value from the input Map
456          *
457          * @param inputMap
458          *            The input Map
459          * @return Map of property name against String property value
460          */
461         private Map<String, String> populateStringProperties(Map<String, Property> inputMap) {
462                 return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
463                                 e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString()));
464         }
465
466         /**
467          * @param idTypeStore
468          * @param resourceModel
469          * @param resourceNodeTemplates
470          */
471         private void processResourceModels(Map<String, String> idTypeStore, Model resourceModel,
472                         List<NodeTemplate> resourceNodeTemplates) {
473                 boolean foundProvidingService = false;
474
475                 for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) {
476                         String nodeTypeName = normaliseNodeTypeName(resourceNodeTemplate);
477                         Metadata metaData = resourceNodeTemplate.getMetaData();
478                         String metaDataType = Optional.ofNullable(metaData).map(m -> m.getValue("type")).orElse(nodeTypeName);
479                         Model resourceNode = Model.getModelFor(nodeTypeName, metaDataType);
480                         foundProvidingService |= processModel(idTypeStore, resourceModel, resourceNodeTemplate, nodeTypeName,
481                                         metaData, resourceNode);
482                 }
483
484                 if (resourceModel instanceof AllotedResource && !foundProvidingService) {
485                         throw new IllegalArgumentException(
486                                         String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, resourceModel.getModelId()));
487                 }
488         }
489
490         private boolean processModel(Map<String, String> idTypeStore, Model resourceModel,
491                         NodeTemplate resourceNodeTemplate, String nodeTypeName, Metadata metaData, Model resourceNode) {
492                 boolean foundProvidingService = false;
493                 if (resourceNode instanceof ProvidingService) {
494                         foundProvidingService = true;
495                         processProvidingService(resourceModel, resourceNodeTemplate, resourceNode);
496                 } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) {
497                         if (metaData != null) {
498                                 resourceNode.populateModelIdentificationInformation(metaData.getAllProperties());
499                         }
500                         idTypeStore.put(resourceNode.getModelNameVersionId(), nodeTypeName);
501                         resourceModel.addResource((Resource) resourceNode);
502                 }
503                 return foundProvidingService;
504         }
505
506         private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) {
507                 Map<String, Property> nodeProperties = resourceNodeTemplate.getProperties();
508                 if (nodeProperties.get("providing_service_uuid") == null
509                                 || nodeProperties.get("providing_service_invariant_uuid") == null) {
510                         throw new IllegalArgumentException(
511                                         String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, resourceModel.getModelId()));
512                 }
513                 Map<String, String> properties = populateStringProperties(nodeProperties);
514                 properties.put(VERSION, "1.0");
515                 resourceNode.populateModelIdentificationInformation(properties);
516                 resourceModel.addResource((Resource) resourceNode);
517         }
518 }