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