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