827e55278a1dcee3c5ef201776abe8f588e380b8
[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.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.Properties;
33 import java.util.stream.Collectors;
34 import java.util.stream.Stream;
35
36 import org.onap.aai.babel.logging.LogHelper;
37 import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil;
38 import org.onap.aai.babel.xml.generator.model.AllotedResource;
39 import org.onap.aai.babel.xml.generator.model.InstanceGroup;
40 import org.onap.aai.babel.xml.generator.model.L3NetworkWidget;
41 import org.onap.aai.babel.xml.generator.model.Model;
42 import org.onap.aai.babel.xml.generator.model.ProvidingService;
43 import org.onap.aai.babel.xml.generator.model.Resource;
44 import org.onap.aai.babel.xml.generator.model.VfModule;
45 import org.onap.aai.babel.xml.generator.model.Widget;
46 import org.onap.aai.babel.xml.generator.types.ModelType;
47 import org.onap.aai.cl.api.Logger;
48 import org.onap.sdc.tosca.parser.api.ISdcCsarHelper;
49 import org.onap.sdc.toscaparser.api.Group;
50 import org.onap.sdc.toscaparser.api.NodeTemplate;
51 import org.onap.sdc.toscaparser.api.Property;
52 import org.onap.sdc.toscaparser.api.elements.Metadata;
53
54 public class ArtifactGeneratorToscaParser {
55
56         private static Logger log = LogHelper.INSTANCE;
57
58         public static final String PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE = "artifactgenerator.config";
59         public static final String PROPERTY_GROUP_FILTERS_CONFIG_FILE = "groupfilter.config";
60
61         private static final String GENERATOR_AAI_CONFIGFILE_NOT_FOUND = "Cannot generate artifacts. Artifact Generator Configuration file not found at %s";
62         private static final String GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND = "Cannot generate artifacts. System property %s not configured";
63         private static final String GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING = "Cannot generate artifacts. Providing Service Metadata is missing for allotted resource %s";
64         private static final String GENERATOR_AAI_PROVIDING_SERVICE_MISSING = "Cannot generate artifacts. Providing Service is missing for allotted resource %s";
65
66         // Metadata properties
67         private static final String CATEGORY = "category";
68         private static final String ALLOTTED_RESOURCE = "Allotted Resource";
69         private static final String SUBCATEGORY = "subcategory";
70         private static final String TUNNEL_XCONNECT = "Tunnel XConnect";
71
72         private static final String VERSION = "version";
73
74         private ISdcCsarHelper csarHelper;
75
76         /**
77          * Constructs using csarHelper
78          *
79          * @param csarHelper
80          *            The csar helper
81          */
82         public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) {
83                 this.csarHelper = csarHelper;
84         }
85
86         /**
87          * Returns the artifact description
88          *
89          * @param model
90          *            the artifact model
91          * @return the artifact model's description
92          */
93         public static String getArtifactDescription(Model model) {
94                 String artifactDesc = model.getModelDescription();
95                 if (model.getModelType().equals(ModelType.SERVICE)) {
96                         artifactDesc = "AAI Service Model";
97                 } else if (model.getModelType().equals(ModelType.RESOURCE)) {
98                         artifactDesc = "AAI Resource Model";
99                 }
100                 return artifactDesc;
101         }
102
103         /**
104          * Initialises the widget configuration.
105          *
106          * @throws IOException
107          */
108         public static void initWidgetConfiguration() throws IOException {
109                 log.debug("Getting Widget Configuration");
110                 String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE);
111                 if (configLocation != null) {
112                         File file = new File(configLocation);
113                         if (file.exists()) {
114                                 Properties properties = new Properties();
115                                 properties.load(new FileInputStream(file));
116                                 WidgetConfigurationUtil.setConfig(properties);
117                         } else {
118                                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
119                         }
120                 } else {
121                         throw new IllegalArgumentException(
122                                         String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE));
123                 }
124         }
125
126         /**
127          * Initialises the group filter configuration.
128          *
129          * @throws IOException
130          */
131         public static void initGroupFilterConfiguration() throws IOException {
132                 log.debug("Getting Filter Tyoes Configuration");
133                 String configLocation = System.getProperty(PROPERTY_GROUP_FILTERS_CONFIG_FILE);
134                 if (configLocation != null) {
135                         File file = new File(configLocation);
136                         if (file.exists()) {
137                                 Properties properties = new Properties();
138                                 properties.load(new FileInputStream(file));
139                                 WidgetConfigurationUtil.setFilterConfig(properties);
140                         } else {
141                                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
142                         }
143                 } else {
144                         throw new IllegalArgumentException(
145                                         String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_GROUP_FILTERS_CONFIG_FILE));
146                 }
147         }
148
149         /**
150          * Process groups for this service node, according to the defined filter.
151          *
152          * @param resourceModel
153          * @param serviceNodeTemplate
154          * @return resources for which XML Models should be generated
155          */
156         public List<Resource> processInstanceGroups(Model resourceModel, NodeTemplate serviceNodeTemplate) {
157                 List<Resource> resources = new ArrayList<>();
158                 if (serviceNodeTemplate.getSubMappingToscaTemplate() != null) {
159                         List<Group> serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate);
160                         for (Group group : serviceGroups) {
161                                 if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) {
162                                         resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(),
163                                                         group.getMetadata().getAllProperties(), group.getProperties()));
164                                 }
165                         }
166                 }
167                 return resources;
168         }
169
170         /**
171          * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are
172          * duplicate keys then the TOSCA Property value takes precedence.
173          *
174          * @param stringProps
175          *            initial Map of String property values (e.g. from the TOSCA YAML metadata section)
176          * @param toscaProps
177          *            Map of TOSCA Property Type Object values to merge in (or overwrite)
178          * @return a Map of the property values converted to String
179          */
180         public Map<String, String> mergeProperties(Map<String, String> stringProps, Map<String, Property> toscaProps) {
181                 Map<String, String> props = new HashMap<>(stringProps);
182                 toscaProps.forEach((key, toscaProp) -> props.put(key,
183                                 toscaProp.getValue() == null ? "" : toscaProp.getValue().toString()));
184                 return props;
185         }
186
187         public Resource createInstanceGroupModel(Map<String, String> properties) {
188                 Resource groupModel = new InstanceGroup();
189                 groupModel.populateModelIdentificationInformation(properties);
190                 return groupModel;
191         }
192
193         /**
194          * @param model
195          * @param relation
196          */
197         public void addRelatedModel(final Model model, final Model relation) {
198                 if (relation instanceof Resource) {
199                         model.addResource((Resource) relation);
200                 } else {
201                         model.addWidget((Widget) relation);
202                 }
203         }
204
205         public String normaliseNodeTypeName(NodeTemplate nodeType) {
206                 String nodeTypeName = nodeType.getType();
207                 Metadata metadata = nodeType.getMetaData();
208                 if (metadata != null && hasAllottedResource(metadata.getAllProperties())) {
209                         if (nodeType.getType().contains("org.openecomp.resource.vf.")) {
210                                 nodeTypeName = "org.openecomp.resource.vf.allottedResource";
211                         }
212                         if (nodeType.getType().contains("org.openecomp.resource.vfc.")) {
213                                 nodeTypeName = "org.openecomp.resource.vfc.AllottedResource";
214                         }
215                 }
216                 return nodeTypeName;
217         }
218
219         public boolean hasAllottedResource(Map<String, String> metadata) {
220                 return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY));
221         }
222
223         public boolean hasSubCategoryTunnelXConnect(Map<String, String> metadata) {
224                 return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY));
225         }
226
227         /**
228          * Process TOSCA Group information for VF Modules.
229          *
230          * @param resources
231          * @param model
232          * @param serviceNode
233          */
234         public void processVfModules(List<Resource> resources, Model resourceModel, NodeTemplate serviceNode) {
235                 // Get the customisation UUID for each VF node and use it to get its Groups
236                 String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNode);
237                 List<Group> serviceGroups = csarHelper.getVfModulesByVf(uuid);
238
239                 // Process each VF Group
240                 for (Group serviceGroup : serviceGroups) {
241                         Model groupModel = Model.getModelFor(serviceGroup.getType());
242                         if (groupModel instanceof VfModule) {
243                                 processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel);
244                         }
245                 }
246         }
247
248         /**
249          * @param resourceModel
250          * @param resourceNodeTemplates
251          */
252         public void processResourceModels(Model resourceModel, List<NodeTemplate> resourceNodeTemplates) {
253                 boolean foundProvidingService = false;
254
255                 for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) {
256                         String nodeTypeName = normaliseNodeTypeName(resourceNodeTemplate);
257                         Metadata metaData = resourceNodeTemplate.getMetaData();
258                         String metaDataType = Optional.ofNullable(metaData).map(m -> m.getValue("type")).orElse(nodeTypeName);
259                         Model resourceNode = Model.getModelFor(nodeTypeName, metaDataType);
260                         foundProvidingService |= processModel(resourceModel, resourceNodeTemplate, metaData, resourceNode);
261                 }
262
263                 if (resourceModel instanceof AllotedResource && !foundProvidingService) {
264                         final String modelInvariantId = resourceModel.getModelId();
265                         throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING,
266                                         modelInvariantId == null ? "<null ID>" : modelInvariantId));
267                 }
268         }
269
270         /**
271          * Create an Instance Group Model and populate it with the supplied data.
272          *
273          * @param resourceModel
274          *            the Resource node template Model
275          * @param memberNodes
276          *            the Resources and Widgets belonging to the Group
277          * @param metaProperties
278          *            the metadata of the Group
279          * @param properties
280          *            the properties of the Group
281          * @return the Instance Group and Member resource models
282          */
283         private List<Resource> processInstanceGroup(Model resourceModel, ArrayList<NodeTemplate> memberNodes,
284                         Map<String, String> metaProperties, Map<String, Property> properties) {
285                 Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties));
286                 resourceModel.addResource(groupModel);
287                 List<Resource> resources = Stream.of(groupModel).collect(Collectors.toList());
288
289                 if (memberNodes != null && !memberNodes.isEmpty()) {
290                         resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel));
291                 }
292
293                 return resources;
294         }
295
296         /**
297          * @param memberNodes
298          * @param groupModel
299          * @return
300          */
301         private List<Resource> generateResourcesAndWidgets(final ArrayList<NodeTemplate> memberNodes,
302                         final Resource groupModel) {
303                 List<Resource> resources = new ArrayList<>();
304                 for (NodeTemplate nodeTemplate : memberNodes) {
305                         String nodeTypeName = normaliseNodeTypeName(nodeTemplate);
306                         Model memberModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
307                         memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
308
309                         log.debug(String.format("Generating grouped %s (%s) from TOSCA type %s",
310                                         memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName));
311
312                         addRelatedModel(groupModel, memberModel);
313                         if (memberModel instanceof Resource) {
314                                 resources.add((Resource) memberModel);
315                         }
316                 }
317                 return resources;
318         }
319
320         private void processVfModule(List<Resource> resources, Model vfModel, Group groupDefinition,
321                         NodeTemplate serviceNode, VfModule groupModel) {
322                 groupModel.populateModelIdentificationInformation(
323                                 mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties()));
324
325                 processVfModuleGroup(groupModel, csarHelper.getMembersOfVfModule(serviceNode, groupDefinition));
326
327                 vfModel.addResource(groupModel); // Add group (VfModule) to the (VF) model
328                 // Check if we have already encountered the same VfModule across all the artifacts
329                 if (!resources.contains(groupModel)) {
330                         resources.add(groupModel);
331                 }
332         }
333
334         private void processVfModuleGroup(VfModule groupModel, List<NodeTemplate> members) {
335                 if (members != null && !members.isEmpty()) {
336                         // Get names of the members of the service group
337                         List<String> memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList());
338                         groupModel.setMembers(memberNames);
339                         for (NodeTemplate member : members) {
340                                 processGroupMembers(groupModel, member);
341                         }
342                 }
343         }
344
345         private void processGroupMembers(Model group, NodeTemplate member) {
346                 Model resourceNode;
347                 // L3-network inside vf-module to be generated as Widget a special handling.
348                 if (member.getType().contains("org.openecomp.resource.vl")) {
349                         resourceNode = new L3NetworkWidget();
350                 } else {
351                         resourceNode = Model.getModelFor(member.getType());
352                 }
353                 if (resourceNode != null && !(resourceNode instanceof Resource)) {
354                         Widget widget = (Widget) resourceNode;
355                         widget.addKey(member.getName());
356                         // Add the widget element encountered to the Group model
357                         group.addWidget(widget);
358                 }
359         }
360
361         /**
362          * Create a Map of property name against String property value from the input Map
363          *
364          * @param inputMap
365          *            The input Map
366          * @return Map of property name against String property value
367          */
368         private Map<String, String> populateStringProperties(Map<String, Property> inputMap) {
369                 return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
370                                 e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString()));
371         }
372
373         /**
374          * @param resourceModel
375          * @param resourceNodeTemplate
376          * @param metaData
377          * @param resourceNode
378          * @return
379          */
380         private boolean processModel(Model resourceModel, NodeTemplate resourceNodeTemplate, Metadata metaData,
381                         Model resourceNode) {
382                 boolean foundProvidingService = false;
383                 if (resourceNode instanceof ProvidingService) {
384                         foundProvidingService = true;
385                         processProvidingService(resourceModel, resourceNodeTemplate, resourceNode);
386                 } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) {
387                         if (metaData != null) {
388                                 resourceNode.populateModelIdentificationInformation(metaData.getAllProperties());
389                         }
390                         resourceModel.addResource((Resource) resourceNode);
391                 }
392                 return foundProvidingService;
393         }
394
395         private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) {
396                 Map<String, Property> nodeProperties = resourceNodeTemplate.getProperties();
397                 if (nodeProperties.get("providing_service_uuid") == null
398                                 || nodeProperties.get("providing_service_invariant_uuid") == null) {
399                         throw new IllegalArgumentException(
400                                         String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, resourceModel.getModelId()));
401                 }
402                 Map<String, String> properties = populateStringProperties(nodeProperties);
403                 properties.put(VERSION, "1.0");
404                 resourceNode.populateModelIdentificationInformation(properties);
405                 resourceModel.addResource((Resource) resourceNode);
406         }
407 }