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