ce1c35250d2a1a8bbf0b54a17c4cc02ec15286b7
[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-2019 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 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.parser;
23
24 import com.google.gson.Gson;
25 import com.google.gson.JsonSyntaxException;
26 import java.io.BufferedReader;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileReader;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Optional;
36 import java.util.Properties;
37 import java.util.stream.Collectors;
38 import java.util.stream.Stream;
39 import org.onap.aai.babel.logging.LogHelper;
40 import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException;
41 import org.onap.aai.babel.xml.generator.data.GroupConfiguration;
42 import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil;
43 import org.onap.aai.babel.xml.generator.model.Model;
44 import org.onap.aai.babel.xml.generator.model.Resource;
45 import org.onap.aai.babel.xml.generator.model.Widget;
46 import org.onap.aai.babel.xml.generator.model.Widget.Type;
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 /**
55  * Wrapper for the sdc-tosca parser.
56  *
57  */
58 public class ArtifactGeneratorToscaParser {
59
60     private static Logger log = LogHelper.INSTANCE;
61
62     public static final String PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE = "artifactgenerator.config";
63     public static final String PROPERTY_TOSCA_MAPPING_FILE = "tosca.mappings.config";
64
65     public static final String GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND =
66             "Cannot generate artifacts. System property %s not configured";
67
68     private static final String GENERATOR_AAI_CONFIGFILE_NOT_FOUND =
69             "Cannot generate artifacts. Artifact Generator Configuration file not found at %s";
70     private static final String GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING =
71             "Cannot generate artifacts. Providing Service Metadata is missing for allotted resource %s";
72     private static final String GENERATOR_AAI_PROVIDING_SERVICE_MISSING =
73             "Cannot generate artifacts. Providing Service is missing for allotted resource %s";
74
75     // Metadata properties
76     private static final String CATEGORY = "category";
77     private static final String ALLOTTED_RESOURCE = "Allotted Resource";
78     private static final String SUBCATEGORY = "subcategory";
79     private static final String TUNNEL_XCONNECT = "Tunnel XConnect";
80
81     private static final String VERSION = "version";
82
83     private ISdcCsarHelper csarHelper;
84
85     /**
86      * Constructs using csarHelper
87      *
88      * @param csarHelper
89      *            The csar helper
90      */
91     public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) {
92         this.csarHelper = csarHelper;
93     }
94
95     /**
96      * Get or create the artifact description.
97      *
98      * @param model
99      *            the artifact model
100      * @return the artifact model's description
101      */
102     public static String getArtifactDescription(Model model) {
103         switch (model.getModelType()) {
104             case SERVICE:
105                 return "AAI Service Model";
106             case RESOURCE:
107                 return "AAI Resource Model";
108             default:
109                 return model.getModelDescription();
110         }
111     }
112
113     /**
114      * Initializes the Widget to UUID mapping configuration.
115      * 
116      * @throws IOException
117      *             if an error occurs reading the configuration properties
118      */
119     public static void initWidgetConfiguration() throws IOException {
120         log.debug("Getting Widget Configuration");
121         String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE);
122         if (configLocation != null) {
123             File file = new File(configLocation);
124             if (file.exists()) {
125                 Properties properties = new Properties();
126                 properties.load(new FileInputStream(file));
127                 WidgetConfigurationUtil.setConfig(properties);
128             } else {
129                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
130             }
131         } else {
132             throw new IllegalArgumentException(
133                     String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE));
134         }
135     }
136
137     /**
138      * Initializes the group filtering and TOSCA to Widget mapping configuration.
139      * 
140      * @param configLocation
141      *            the pathname to the JSON mappings file
142      * @throws IOException
143      *             if the file content could not be read successfully
144      */
145     public static void initToscaMappingsConfiguration(String configLocation) throws IOException {
146         log.debug("Getting TOSCA Mappings Configuration");
147         File file = new File(configLocation);
148         if (!file.exists()) {
149             throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
150         }
151
152         GroupConfiguration config;
153
154         try (BufferedReader bufferedReader = new BufferedReader(new FileReader(configLocation))) {
155             config = new Gson().fromJson(bufferedReader, GroupConfiguration.class);
156         } catch (JsonSyntaxException e) {
157             throw new IOException("Invalid Mappings Configuration " + configLocation, e);
158         }
159
160         if (config == null) {
161             throw new IOException("There is no content for the Mappings Configuration " + configLocation);
162         }
163
164         WidgetConfigurationUtil.setSupportedInstanceGroups(config.getInstanceGroupTypes());
165         WidgetConfigurationUtil.setWidgetTypes(config.getWidgetTypes());
166         WidgetConfigurationUtil.setWidgetMappings(config.getWidgetMappings());
167     }
168
169     /**
170      * Process groups for this service node, according to the defined filter.
171      *
172      * @param resourceModel
173      * @param serviceNodeTemplate
174      * @return resources for which XML Models should be generated
175      * @throws XmlArtifactGenerationException
176      *             if there is no configuration defined for a member Widget of an instance group
177      */
178     public List<Resource> processInstanceGroups(Model resourceModel, NodeTemplate serviceNodeTemplate)
179             throws XmlArtifactGenerationException {
180         List<Resource> resources = new ArrayList<>();
181         if (serviceNodeTemplate.getSubMappingToscaTemplate() != null) {
182             List<Group> serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate);
183             for (Group group : serviceGroups) {
184                 if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) {
185                     resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(),
186                             group.getMetadata().getAllProperties(), group.getProperties()));
187                 }
188             }
189         }
190         return resources;
191     }
192
193     /**
194      * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are
195      * duplicate keys then the TOSCA Property value takes precedence.
196      *
197      * @param stringProps
198      *            initial Map of String property values (e.g. from the TOSCA YAML metadata section)
199      * @param toscaProps
200      *            Map of TOSCA Property Type Object values to merge in (or overwrite)
201      * @return a Map of the property values converted to String
202      */
203     public Map<String, String> mergeProperties(Map<String, String> stringProps, Map<String, Property> toscaProps) {
204         Map<String, String> props = new HashMap<>(stringProps);
205         toscaProps.forEach((key, toscaProp) -> props.put(key,
206                 toscaProp.getValue() == null ? "" : toscaProp.getValue().toString()));
207         return props;
208     }
209
210     public Resource createInstanceGroupModel(Map<String, String> properties) {
211         Resource groupModel = new Resource(Type.INSTANCE_GROUP, true);
212         groupModel.populateModelIdentificationInformation(properties);
213         return groupModel;
214     }
215
216     /**
217      * Add the resource/widget to the specified model.
218      * 
219      * @param model
220      * @param relation
221      *            resource or widget model to add
222      * @throws XmlArtifactGenerationException
223      *             if the relation is a widget and there is no configuration defined for the relation's widget type
224      */
225     public void addRelatedModel(final Model model, final Resource relation) throws XmlArtifactGenerationException {
226         if (relation.isResource()) {
227             model.addResource(relation);
228         } else {
229             model.addWidget(Widget.getWidget(relation.getWidgetType()));
230         }
231     }
232
233     public boolean hasAllottedResource(Map<String, String> metadata) {
234         return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY));
235     }
236
237     public boolean hasSubCategoryTunnelXConnect(Map<String, String> metadata) {
238         return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY));
239     }
240
241     /**
242      * Process TOSCA Group information for VF Modules.
243      *
244      * @param resources
245      * @param model
246      * @param serviceNode
247      * @throws XmlArtifactGenerationException
248      */
249     public void processVfModules(List<Resource> resources, Model resourceModel, NodeTemplate serviceNode)
250             throws XmlArtifactGenerationException {
251         // Get the customisation UUID for each VF node and use it to get its Groups
252         String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNode);
253         List<Group> serviceGroups = csarHelper.getVfModulesByVf(uuid);
254
255         // Process each VF Group
256         for (Group serviceGroup : serviceGroups) {
257             Model groupModel = Model.getModelFor(serviceGroup.getType());
258             if (groupModel.getWidgetType() == Type.VFMODULE) {
259                 processVfModule(resources, resourceModel, serviceGroup, serviceNode, (Resource) groupModel);
260             }
261         }
262     }
263
264     /**
265      * @param resourceModel
266      * @param resourceNodeTemplates
267      */
268     public void processResourceModels(Model resourceModel, List<NodeTemplate> resourceNodeTemplates) {
269         boolean foundProvidingService = false;
270
271         for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) {
272             String nodeTypeName = resourceNodeTemplate.getType();
273             Metadata metadata = resourceNodeTemplate.getMetaData();
274             String metaDataType = Optional.ofNullable(metadata).map(m -> m.getValue("type")).orElse(nodeTypeName);
275             Resource model = Model.getModelFor(nodeTypeName, metaDataType);
276
277             if (metadata != null && hasAllottedResource(metadata.getAllProperties())
278                     && model.getWidgetType() == Type.VSERVER) {
279                 model = new Resource(Type.ALLOTTED_RESOURCE, false);
280                 Map<String, Object> props = new HashMap<>();
281                 props.put("providingService", true);
282                 model.setProperties(props);
283             }
284
285             foundProvidingService |= processModel(resourceModel, metadata, model, resourceNodeTemplate.getProperties());
286         }
287
288         if (resourceModel.getWidgetType() == Type.ALLOTTED_RESOURCE && !foundProvidingService) {
289             final String modelInvariantId = resourceModel.getModelId();
290             throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING,
291                     modelInvariantId == null ? "<null ID>" : modelInvariantId));
292         }
293     }
294
295     /**
296      * Create an Instance Group Model and populate it with the supplied data.
297      *
298      * @param resourceModel
299      *            the Resource node template Model
300      * @param memberNodes
301      *            the Resources and Widgets belonging to the Group
302      * @param metaProperties
303      *            the metadata of the Group
304      * @param properties
305      *            the properties of the Group
306      * @return the Instance Group and Member resource models
307      * @throws XmlArtifactGenerationException
308      *             if there is no configuration defined for one of the member Widgets
309      */
310     private List<Resource> processInstanceGroup(Model resourceModel, ArrayList<NodeTemplate> memberNodes,
311             Map<String, String> metaProperties, Map<String, Property> properties)
312             throws XmlArtifactGenerationException {
313         Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties));
314         resourceModel.addResource(groupModel);
315         List<Resource> resources = Stream.of(groupModel).collect(Collectors.toList());
316
317         if (memberNodes != null && !memberNodes.isEmpty()) {
318             resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel));
319         }
320
321         return resources;
322     }
323
324     /**
325      * @param memberNodes
326      * @param groupModel
327      * @return a list of Resources
328      * @throws XmlArtifactGenerationException
329      *             if a member node template is a widget and there is no configuration defined for that relation's
330      *             widget type
331      */
332     private List<Resource> generateResourcesAndWidgets(final ArrayList<NodeTemplate> memberNodes,
333             final Resource groupModel) throws XmlArtifactGenerationException {
334         log.debug(String.format("Processing member nodes for Group %s (invariant UUID %s)", //
335                 groupModel.getModelName(), groupModel.getModelId()));
336
337         List<Resource> resources = new ArrayList<>();
338
339         for (NodeTemplate nodeTemplate : memberNodes) {
340             String nodeTypeName = nodeTemplate.getType();
341             final String metadataType = nodeTemplate.getMetaData().getValue("type");
342
343             log.debug(String.format("Get model for %s (metadata type %s)", nodeTypeName, metadataType));
344             Resource memberModel = Model.getModelFor(nodeTypeName, metadataType);
345
346             if (memberModel != null) {
347                 memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
348
349                 log.debug(String.format("Generating grouped %s (%s) from TOSCA type %s",
350                         memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName));
351
352                 addRelatedModel(groupModel, memberModel);
353                 if (memberModel.isResource()) {
354                     resources.add(memberModel);
355                 }
356             }
357         }
358         return resources;
359     }
360
361     private void processVfModule(List<Resource> resources, Model vfModel, Group groupDefinition,
362             NodeTemplate serviceNode, Resource groupModel) throws XmlArtifactGenerationException {
363         groupModel.populateModelIdentificationInformation(
364                 mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties()));
365
366         processVfModuleGroup(groupModel, csarHelper.getMembersOfVfModule(serviceNode, groupDefinition));
367
368         vfModel.addResource(groupModel); // Add group (VfModule) to the (VF) model
369         // Check if we have already encountered the same VfModule across all the artifacts
370         if (!resources.contains(groupModel)) {
371             resources.add(groupModel);
372         }
373     }
374
375     private void processVfModuleGroup(Resource groupModel, List<NodeTemplate> members)
376             throws XmlArtifactGenerationException {
377         if (members != null && !members.isEmpty()) {
378             // Get names of the members of the service group
379             List<String> memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList());
380             groupModel.setMembers(memberNames);
381             for (NodeTemplate member : members) {
382                 processGroupMembers(groupModel, member);
383             }
384         }
385     }
386
387     /**
388      * Process the Widget members of a VF Module Group
389      * 
390      * @param group
391      * @param member
392      * @throws XmlArtifactGenerationException
393      */
394     private void processGroupMembers(Resource group, NodeTemplate member) throws XmlArtifactGenerationException {
395         Resource resource = Model.getModelFor(member.getType());
396
397         log.debug(member.getType() + " mapped to " + resource);
398
399         if (resource.getWidgetType() == Type.L3_NET) {
400             // An l3-network inside a vf-module is treated as a Widget
401             resource.setIsResource(false);
402         }
403
404         if (!resource.isResource()) {
405             Widget widget = Widget.getWidget(resource.getWidgetType());
406             widget.addKey(member.getName());
407             // Add the widget element encountered to the Group model
408             group.addWidget(widget);
409         }
410     }
411
412     /**
413      * Create a Map of property name against String property value from the input Map
414      *
415      * @param inputMap
416      *            The input Map
417      * @return Map of property name against String property value
418      */
419     private Map<String, String> populateStringProperties(Map<String, Property> inputMap) {
420         return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
421                 e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString()));
422     }
423
424     /**
425      * If the specified resourceNode is a type of Resource, add it to the specified resourceModel. If the Resource type
426      * is ProvidingService then return true, otherwise return false.
427      *
428      * @param resourceModel
429      *            parent Resource
430      * @param metaData
431      *            for populating the Resource IDs
432      * @param resourceNode
433      *            any Model (will be ignored if not a Resource)
434      * @param nodeProperties
435      *            the node properties
436      * @return whether or not a ProvidingService was processed
437      */
438     private boolean processModel(Model resourceModel, Metadata metaData, Resource resourceNode,
439             Map<String, Property> nodeProperties) {
440         boolean foundProvidingService = resourceNode != null
441                 && (boolean) Optional.ofNullable(resourceNode.getProperties().get("providingService")).orElse(false);
442
443         if (foundProvidingService) {
444             processProvidingService(resourceModel, resourceNode, nodeProperties);
445         } else if (resourceNode != null && resourceNode.isResource()
446                 && resourceNode.getWidgetType() != Widget.Type.L3_NET) {
447             if (metaData != null) {
448                 resourceNode.populateModelIdentificationInformation(metaData.getAllProperties());
449             }
450             resourceModel.addResource((Resource) resourceNode);
451         }
452         return foundProvidingService;
453     }
454
455     private void processProvidingService(Model resourceModel, Resource resourceNode,
456             Map<String, Property> nodeProperties) {
457         if (nodeProperties == null || nodeProperties.get("providing_service_uuid") == null
458                 || nodeProperties.get("providing_service_invariant_uuid") == null) {
459             throw new IllegalArgumentException(
460                     String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, resourceModel.getModelId()));
461         }
462         Map<String, String> properties = populateStringProperties(nodeProperties);
463         properties.put(VERSION, "1.0");
464         resourceNode.populateModelIdentificationInformation(properties);
465         resourceModel.addResource(resourceNode);
466     }
467 }