Add support for ResourceInstanceGroup
[aai/babel.git] / src / main / java / org / onap / aai / babel / parser / ArtifactGeneratorToscaParser.java
index c769e9a..80b75b0 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * ============LICENSE_START=======================================================
+ * ============LICENSE_START=======================================================
  * org.onap.aai
  * ================================================================================
  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
@@ -24,14 +24,18 @@ package org.onap.aai.babel.parser;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.stream.Collectors;
+import org.onap.aai.babel.logging.ApplicationMsgs;
 import org.onap.aai.babel.logging.LogHelper;
 import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil;
 import org.onap.aai.babel.xml.generator.model.AllotedResource;
+import org.onap.aai.babel.xml.generator.model.InstanceGroup;
 import org.onap.aai.babel.xml.generator.model.L3NetworkWidget;
 import org.onap.aai.babel.xml.generator.model.Model;
 import org.onap.aai.babel.xml.generator.model.ProvidingService;
@@ -46,17 +50,19 @@ import org.onap.sdc.tosca.parser.api.ISdcCsarHelper;
 import org.onap.sdc.toscaparser.api.Group;
 import org.onap.sdc.toscaparser.api.NodeTemplate;
 import org.onap.sdc.toscaparser.api.Property;
+import org.onap.sdc.toscaparser.api.elements.Metadata;
 
 public class ArtifactGeneratorToscaParser {
 
     private static Logger log = LogHelper.INSTANCE;
 
     public static final String PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE = "artifactgenerator.config";
+    public static final String PROPERTY_GROUP_FILTERS_CONFIG_FILE = "groupfilter.config";
 
     private static final String GENERATOR_AAI_CONFIGFILE_NOT_FOUND =
             "Cannot generate artifacts. Artifact Generator Configuration file not found at %s";
     private static final String GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND =
-            "Cannot generate artifacts. artifactgenerator.config system property not configured";
+            "Cannot generate artifacts. System property %s not configured";
     private static final String GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING =
             "Cannot generate artifacts. Providing Service Metadata is missing for allotted resource %s";
     private static final String GENERATOR_AAI_PROVIDING_SERVICE_MISSING =
@@ -115,7 +121,52 @@ public class ArtifactGeneratorToscaParser {
                 throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
             }
         } else {
-            throw new IllegalArgumentException(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND);
+            throw new IllegalArgumentException(
+                    String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE));
+        }
+    }
+
+    /**
+     * Initialises the group filter configuration.
+     *
+     * @throws IOException
+     */
+    public static void initGroupFilterConfiguration() throws IOException {
+        log.debug("Getting Filter Tyoes Configuration");
+        String configLocation = System.getProperty(PROPERTY_GROUP_FILTERS_CONFIG_FILE);
+        if (configLocation != null) {
+            File file = new File(configLocation);
+            if (file.exists()) {
+                Properties properties = new Properties();
+                properties.load(new FileInputStream(file));
+                WidgetConfigurationUtil.setFilterConfig(properties);
+            } else {
+                throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation));
+            }
+        } else {
+            throw new IllegalArgumentException(
+                    String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_GROUP_FILTERS_CONFIG_FILE));
+        }
+    }
+
+    /**
+     * Process the service TOSCA.
+     *
+     * @param service model of the service artifact
+     * @param idTypeStore ID->Type mapping
+     * @param nodeTemplates a list of service nodes
+     *
+     */
+    public void processServiceTosca(Service service, Map<String, String> idTypeStore,
+            List<NodeTemplate> nodeTemplates) {
+        log.debug("Processing (TOSCA) Service object");
+
+        for (NodeTemplate nodeTemplate : nodeTemplates) {
+            if (nodeTemplate.getMetaData() != null) {
+                addNodeToService(idTypeStore, service, nodeTemplate);
+            } else {
+                log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName());
+            }
         }
     }
 
@@ -124,55 +175,183 @@ public class ArtifactGeneratorToscaParser {
      *
      * @param serviceNodes input Service Node Templates
      * @param idTypeStore ID->Type mapping
+     *
      * @return the processed resource models
      */
     public List<Resource> processResourceToscas(List<NodeTemplate> serviceNodes, Map<String, String> idTypeStore) {
         List<Resource> resources = new LinkedList<>();
         for (NodeTemplate serviceNode : serviceNodes) {
-            List<NodeTemplate> resourceNodes = csarHelper.getNodeTemplateChildren(serviceNode);
+            if (serviceNode.getMetaData() != null) {
+                resources.addAll(processResourceTosca(idTypeStore, serviceNode,
+                        csarHelper.getNodeTemplateChildren(serviceNode)));
+            } else {
+                log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, serviceNode.getName());
+            }
+        }
+        return resources;
+    }
 
-            String resourceUuId = serviceNode.getMetaData().getValue("UUID");
-            String mapValue = idTypeStore.get(resourceUuId);
-            if (mapValue != null) {
-                Model model = Model.getModelFor(idTypeStore.get(serviceNode.getMetaData().getValue("UUID")));
+    /**
+     * @param idTypeStore ID->Type mapping
+     * @param serviceNode
+     * @param resourceNodes
+     * @return the processed resource models
+     */
+    private List<Resource> processResourceTosca(Map<String, String> idTypeStore, NodeTemplate serviceNode,
+            List<NodeTemplate> resourceNodes) {
+        List<Resource> resources = new LinkedList<>();
+        String resourceUuId = serviceNode.getMetaData().getValue("UUID");
+        String nodeTypeName = idTypeStore.get(resourceUuId);
+        if (nodeTypeName != null) {
+            Model resourceModel = Model.getModelFor(nodeTypeName, serviceNode.getMetaData().getValue("type"));
 
-                log.debug("Inside Resource artifact generation for resource");
-                Map<String, String> serviceMetadata = serviceNode.getMetaData().getAllProperties();
-                model.populateModelIdentificationInformation(serviceMetadata);
+            log.debug("Processing resource " + nodeTypeName + ": " + resourceUuId);
+            Map<String, String> serviceMetadata = serviceNode.getMetaData().getAllProperties();
+            resourceModel.populateModelIdentificationInformation(serviceMetadata);
 
-                // Found model from the type store so removing the same
-                idTypeStore.remove(model.getModelNameVersionId());
-                processVfTosca(idTypeStore, model, resourceNodes);
+            idTypeStore.remove(resourceModel.getModelNameVersionId());
+            processResourceModels(idTypeStore, resourceModel, resourceNodes);
 
-                // Process group information from tosca for vfModules
-                if (csarHelper.getServiceVfList() != null) {
-                    processVfModules(resources, model, serviceNode);
-                }
+            if (csarHelper.getServiceVfList() != null) {
+                processVfModules(resources, resourceModel, serviceNode);
+            }
+
+            if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) {
+                resourceModel.addWidget(new TunnelXconnectWidget());
+            }
+
+            resources.addAll(processInstanceGroups(resourceModel, serviceNode));
+            resources.add((Resource) resourceModel);
+        }
+        return resources;
+    }
 
-                if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) {
-                    model.addWidget(new TunnelXconnectWidget());
+    /**
+     * Process groups for this service node, according to the defined filter.
+     *
+     * @param resourceModel
+     * @param serviceNode
+     * @return resources for which XML Models should be generated
+     */
+    List<Resource> processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) {
+        List<Resource> resources = new ArrayList<>();
+        if (csarHelper.getNodeTemplateByName(serviceNode.getName()).getSubMappingToscaTemplate() != null) {
+            List<Group> serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode);
+            for (Group group : serviceGroups) {
+                if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) {
+                    resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(),
+                            group.getMetadata().getAllProperties(), group.getProperties()));
                 }
-                resources.add((Resource) model);
             }
         }
         return resources;
     }
 
-    private void processVfModules(List<Resource> resources, Model model, NodeTemplate serviceNode) {
+    /**
+     * Create an Instance Group Model and populate it with the supplied data.
+     *
+     * @param resourceModel the Resource node template Model
+     * @param memberNodes the Resources and Widgets belonging to the Group
+     * @param metaProperties the metadata of the Group
+     * @param properties the properties of the Group
+     * @return the Instance Group and Member resource models
+     */
+    private List<Resource> processInstanceGroup(Model resourceModel, ArrayList<NodeTemplate> memberNodes,
+            Map<String, String> metaProperties, Map<String, Property> properties) {
+        List<Resource> resources = new ArrayList<>();
+
+        Resource groupModel = new InstanceGroup();
+        groupModel.populateModelIdentificationInformation(metaProperties);
+        groupModel.populateModelIdentificationInformation(populateStringProperties(properties));
+
+        resourceModel.addResource(groupModel);
+        resources.add(groupModel);
+
+        if (memberNodes != null && !memberNodes.isEmpty()) {
+            resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel));
+        }
+
+        return resources;
+    }
+
+    /**
+     * @param memberNodes
+     * @param groupModel
+     * @return
+     */
+    private List<Resource> generateResourcesAndWidgets(final ArrayList<NodeTemplate> memberNodes,
+            final Resource groupModel) {
+        List<Resource> resources = new ArrayList<>();
+        for (NodeTemplate nodeTemplate : memberNodes) {
+            String nodeTypeName = normaliseNodeTypeName(nodeTemplate);
+            Model memberModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
+            memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
+
+            log.debug(String.format("Generating grouped %s (%s) from TOSCA type %s",
+                    memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName));
+
+            addRelatedModel(groupModel, memberModel);
+            if (memberModel instanceof Resource) {
+                resources.add((Resource) memberModel);
+            }
+        }
+        return resources;
+    }
+
+    /**
+     * Add the supplied Node Template to the Service, provided that it is a valid Resource or Widget. If the Node
+     * Template is a Resource type, this is also recorded in the supplied nodesById Map.
+     *
+     * @param nodesById a map of Resource node type names, keyed by UUID
+     * @param service the Service to which the Node Template should be added
+     * @param nodeTemplate the Node Template to add (only if this is a Resource or Widget type)
+     */
+    private void addNodeToService(Map<String, String> nodesById, Service service, NodeTemplate nodeTemplate) {
+        String nodeTypeName = normaliseNodeTypeName(nodeTemplate);
+        Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type"));
+        if (model != null) {
+            if (nodeTemplate.getMetaData() != null) {
+                model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties());
+            }
+
+            addRelatedModel(service, model);
+            if (model instanceof Resource) {
+                nodesById.put(model.getModelNameVersionId(), nodeTypeName);
+            }
+        }
+    }
+
+    /**
+     * @param model
+     * @param relation
+     */
+    private void addRelatedModel(final Model model, final Model relation) {
+        if (relation instanceof Resource) {
+            model.addResource((Resource) relation);
+        } else {
+            model.addWidget((Widget) relation);
+        }
+    }
+
+    /**
+     * Process TOSCA Group information for VF Modules.
+     *
+     * @param resources
+     * @param model
+     * @param serviceNode
+     */
+    private void processVfModules(List<Resource> resources, Model resourceModel, NodeTemplate serviceNode) {
         // Get the customisation UUID for each VF node and use it to get its Groups
         String uuid = csarHelper.getNodeTemplateCustomizationUuid(serviceNode);
-
-        // Populate a Map of Group against NodeTemplates that are members of the Group
         List<Group> serviceGroups = csarHelper.getVfModulesByVf(uuid);
 
         // Process each VF Group
         for (Group serviceGroup : serviceGroups) {
             Model groupModel = Model.getModelFor(serviceGroup.getType());
             if (groupModel instanceof VfModule) {
-                processVfModule(resources, model, serviceGroup, serviceNode, (VfModule) groupModel);
+                processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel);
             }
         }
-
     }
 
     private void processVfModule(List<Resource> resources, Model model, Group groupDefinition, NodeTemplate serviceNode,
@@ -193,8 +372,8 @@ public class ArtifactGeneratorToscaParser {
         if (members != null && !members.isEmpty()) {
             List<String> memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList());
             groupModel.setMembers(memberNames);
-            for (NodeTemplate nodeTemplate : members) {
-                processNodeTemplate(groupModel, nodeTemplate);
+            for (NodeTemplate member : members) {
+                processGroupMembers(groupModel, member);
             }
         }
 
@@ -205,61 +384,34 @@ public class ArtifactGeneratorToscaParser {
         }
     }
 
-    private static void processNodeTemplate(Model group, NodeTemplate nodeTemplate) {
+    private void processGroupMembers(Model group, NodeTemplate member) {
         Model resourceNode;
         // L3-network inside vf-module to be generated as Widget a special handling.
-        if (nodeTemplate.getType().contains("org.openecomp.resource.vl")) {
+        if (member.getType().contains("org.openecomp.resource.vl")) {
             resourceNode = new L3NetworkWidget();
         } else {
-            resourceNode = Model.getModelFor(nodeTemplate.getType());
+            resourceNode = Model.getModelFor(member.getType());
         }
         if (resourceNode != null && !(resourceNode instanceof Resource)) {
             Widget widget = (Widget) resourceNode;
-            widget.addKey(nodeTemplate.getName());
+            widget.addKey(member.getName());
             // Add the widget element encountered to the Group model
             group.addWidget(widget);
         }
     }
 
-    /**
-     * Process the service tosca
-     *
-     * @param service model of the service artifact
-     * @param idTypeStore ID->Type mapping
-     * @param nodeTemplates a list of service nodes
-     *
-     */
-    public void processServiceTosca(Service service, Map<String, String> idTypeStore,
-            List<NodeTemplate> nodeTemplates) {
-        log.debug("Inside Service Tosca ");
-        // Get the resource/widgets in the service according to the node-template types
-        for (NodeTemplate node : nodeTemplates) {
-            Model model = Model.getModelFor(correctNodeType(node));
-            if (model != null) {
-                model.populateModelIdentificationInformation(node.getMetaData().getAllProperties());
-                if (model instanceof Resource) {
-                    // Keeping track of resource types and
-                    // their uuid for identification during resource tosca processing
-                    idTypeStore.put(model.getModelNameVersionId(), correctNodeType(node));
-                    service.addResource((Resource) model);
-                } else {
-                    service.addWidget((Widget) model);
-                }
-            }
-        }
-    }
-
-    private String correctNodeType(NodeTemplate nodeType) {
-        String correctedNodeType = nodeType.getType();
-        if (hasAllottedResource(nodeType.getMetaData().getAllProperties())) {
+    private String normaliseNodeTypeName(NodeTemplate nodeType) {
+        String nodeTypeName = nodeType.getType();
+        Metadata metadata = nodeType.getMetaData();
+        if (metadata != null && hasAllottedResource(metadata.getAllProperties())) {
             if (nodeType.getType().contains("org.openecomp.resource.vf.")) {
-                correctedNodeType = "org.openecomp.resource.vf.allottedResource";
+                nodeTypeName = "org.openecomp.resource.vf.allottedResource";
             }
             if (nodeType.getType().contains("org.openecomp.resource.vfc.")) {
-                correctedNodeType = "org.openecomp.resource.vfc.AllottedResource";
+                nodeTypeName = "org.openecomp.resource.vfc.AllottedResource";
             }
         }
-        return correctedNodeType;
+        return nodeTypeName;
     }
 
     private boolean hasAllottedResource(Map<String, String> metadata) {
@@ -281,32 +433,52 @@ public class ArtifactGeneratorToscaParser {
                 e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString()));
     }
 
-    private void processVfTosca(Map<String, String> idTypeStore, Model model, List<NodeTemplate> resourceNodes) {
-        boolean flag = false;
+    private void processResourceModels(Map<String, String> idTypeStore, Model resourceModel,
+            List<NodeTemplate> resourceNodes) {
+        boolean foundProvidingService = false;
 
         for (NodeTemplate resourceNodeTemplate : resourceNodes) {
-            Model resourceNode = Model.getModelFor(correctNodeType(resourceNodeTemplate));
-            if (resourceNode instanceof ProvidingService) {
-                flag = true;
-                Map<String, Property> nodeProperties = resourceNodeTemplate.getProperties();
-                if (nodeProperties.get("providing_service_uuid") == null
-                        || nodeProperties.get("providing_service_invariant_uuid") == null) {
-                    throw new IllegalArgumentException(
-                            String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, model.getModelId()));
-                }
-                Map<String, String> properties = populateStringProperties(nodeProperties);
-                properties.put(VERSION, "1.0");
-                resourceNode.populateModelIdentificationInformation(properties);
-                model.addResource((Resource) resourceNode);
-            } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) {
-                idTypeStore.put(resourceNode.getModelNameVersionId(), correctNodeType(resourceNodeTemplate));
-                model.addResource((Resource) resourceNode);
+            String nodeTypeName = normaliseNodeTypeName(resourceNodeTemplate);
+            Metadata metaData = resourceNodeTemplate.getMetaData();
+            String metaDataType = Optional.ofNullable(metaData).map(m -> m.getValue("type")).orElse(nodeTypeName);
+            Model resourceNode = Model.getModelFor(nodeTypeName, metaDataType);
+            foundProvidingService |= processModel(idTypeStore, resourceModel, resourceNodeTemplate, nodeTypeName,
+                    metaData, resourceNode);
+        }
+
+        if (resourceModel instanceof AllotedResource && !foundProvidingService) {
+            throw new IllegalArgumentException(
+                    String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, resourceModel.getModelId()));
+        }
+    }
+
+    private boolean processModel(Map<String, String> idTypeStore, Model resourceModel,
+            NodeTemplate resourceNodeTemplate, String nodeTypeName, Metadata metaData, Model resourceNode) {
+        boolean foundProvidingService = false;
+        if (resourceNode instanceof ProvidingService) {
+            foundProvidingService = true;
+            processProvidingService(resourceModel, resourceNodeTemplate, resourceNode);
+        } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) {
+            if (metaData != null) {
+                resourceNode.populateModelIdentificationInformation(metaData.getAllProperties());
             }
+            idTypeStore.put(resourceNode.getModelNameVersionId(), nodeTypeName);
+            resourceModel.addResource((Resource) resourceNode);
         }
+        return foundProvidingService;
+    }
 
-        if (model instanceof AllotedResource && !flag) {
+    private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) {
+        Map<String, Property> nodeProperties = resourceNodeTemplate.getProperties();
+        if (nodeProperties.get("providing_service_uuid") == null
+                || nodeProperties.get("providing_service_invariant_uuid") == null) {
             throw new IllegalArgumentException(
-                    String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, model.getModelId()));
+                    String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, resourceModel.getModelId()));
         }
+        Map<String, String> properties = populateStringProperties(nodeProperties);
+        properties.put(VERSION, "1.0");
+        resourceNode.populateModelIdentificationInformation(properties);
+        resourceModel.addResource((Resource) resourceNode);
     }
+
 }