From 34445a97233549a0ebaf4359a733e11112063bc1 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Fri, 9 Nov 2018 11:27:01 +0000 Subject: [PATCH 01/16] Minor formatting fixes Non-functional changes addressing whitespace inconsistencies. Change-Id: Ife84f6b04bf8ce0204409d2f3708ab85b3a3caff Issue-ID: AAI-1884 Signed-off-by: mark.j.leonard --- .../csar/vnfcatalog/VnfVendorImageExtractor.java | 1 - .../xml/generator/api/AaiArtifactGenerator.java | 1 - .../java/org/onap/aai/babel/logging/LogReader.java | 1 - .../aai/babel/xml/generator/model/TestVfModule.java | 1 - .../service_NetworkCollection.csar | Bin 32286 -> 31786 bytes 5 files changed, 4 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java index 97d69f8..c272b35 100644 --- a/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java +++ b/src/main/java/org/onap/aai/babel/csar/vnfcatalog/VnfVendorImageExtractor.java @@ -31,7 +31,6 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; - import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.time.StopWatch; import org.apache.commons.lang3.tuple.ImmutablePair; diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index 505378e..19a08f2 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -26,7 +26,6 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.onap.aai.babel.logging.ApplicationMsgs; diff --git a/src/test/java/org/onap/aai/babel/logging/LogReader.java b/src/test/java/org/onap/aai/babel/logging/LogReader.java index 1e84b62..f18fb6d 100644 --- a/src/test/java/org/onap/aai/babel/logging/LogReader.java +++ b/src/test/java/org/onap/aai/babel/logging/LogReader.java @@ -33,7 +33,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; - import org.apache.commons.lang3.time.StopWatch; import org.junit.Assert; diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java index a07a68f..e9c8c1b 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java @@ -33,7 +33,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Properties; - import org.junit.BeforeClass; import org.junit.Test; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; diff --git a/src/test/resources/compressedArtifacts/service_NetworkCollection.csar b/src/test/resources/compressedArtifacts/service_NetworkCollection.csar index 7a1189966cae1e939d1eb26bdb3ac4091eb728bb..bdac973627300d7ff15ea33fbdf41657a32f10d2 100644 GIT binary patch delta 2896 zcmZXWcTm$=7lspxfha@->Ai(sLNC(XNFacagc1-0ktQH5QUXh)*eHVhKtU`tsVXQ0 zP~iTT*}QkR#0nh1s%B9nmL^Qac@@b%@qq+Ppw$8#?_g|G=wTIyi$C-Gjy z7h>Iqapq3ccKuNLFm|6+mTd&F#2rL@Oda<2(`?%{!+XVd-T|^%rOj=8dlV?phBN0FLSb z$c$-}C}|}E1vl?(d0>A$s-uI*_$fw4$|XY*(ZL4 z_y&)X-kkYb(-nG~W|5E{IxSTHn7-Sv%V>?}W@O?5F@RV=Cmeh%hkm5lbb&!2S7yM` z*Mvm=w>2ROCQ+L~U7`G#9EbC*qB4lb(0fuDc?~~jQVx=)@|-K&47yT|qEHsvUX@<3kGx^h{g_xn zhND-O^_N+x*VVc=*~U?=Tu+RfGm=>^+QW@F%&kxb4KZzMr+BWDWWIz~-im3OG9Aer z%HJL{WY~v>k3p7l0_uF*VL1pj;*5O+ZBjyZCgarlQ|MqD)2>HxJ3wJ{II|E^`blX&0BjwzATat z!B|0YoI3T!vbeI09*bUVV%wb)p#pP-#y^sqOt_dhchx$$7Z-=C&%RQH=Romg?tN+} z!##4vDA$yW) zFQ|J}08IfrOg-}2p1^p3u;=_vQEMdoB>c>wx|kv|(^mpU7txBg#GwbOOKNw%c+T;4 z89p)NNzT#3PX>7e-%mTuqkNhI&vnDBF9=B^S1SgqI;$kdkxP&LgY`3-1&JVfhsrj;%y$jmWY*OrP{>;^YQj`!@{L#WoPDspJAlb@apv3b;%P3t38I!-GUT_{qt zldI!fl6-P8I?X9=@%}*`J~(4>HK3bi+ELQGd37`}x`Z4{xL`Y8rCiR6r^{Vlqg@Om z)*ds(HJ*Y^uj8aEh~W^u6Q-j+O5F=Nx-X}`a{fCMpOU>T-=2S27k*^UN)X@-L7Rl@ zxjLb8vVK`Ud_7miO-dH})2`TzBd5=}dLrKl>-&HODTf|R-rJPg%#qYHVZ_0&cxg`x zeH*;einpN{%edXJE7S(_H#u-l7@fnB)*hF$YkBDRU5n)Odgoq*hl#n0Aksg3zQ*;rwdoquR?R!FG#}X* zNUz$;o2_iE7kP(Ey%d}I2%*n2-wb|+hb>m;8XBDW_*82m>VbdQ&i?g9M^%b#p38a;<80(d|hjJm(p}x8SpCIO#RB$eVT_9KX!J`LvqDeIY4^S{JT@ zC&wHRHH1rE8iPOBTJAB)nDP@PTC3bsoL=uM@ehf3=oEfP9*f2fGR;vPobMPaY)qwV z#T3@fu_zi0(8O<=3W;gOz^CWe`te?Hp?BP>^u=d8^G3%gNn$7GZd*JpfV8+$Ot#9t-U2kuZ zopua-?fHKG&^pvT>KayZ83Tu>*LH>=HR^NRk>dMxJIk6Br@RBf;!ya(77?- zESo&Ed`r@PL$Y62`nc?{+64O3ot{;rj?vyioz*xIhM6bd2fx;uO~fl(iQ;m;MlDK( z3z%Ee=}P-CPw33Q`m!=9ES8b|_fI1r7>QyQZNHls21G^)i~ereN9q8Q1pNvyNc3p6 zyaKrR8k~auz4>*>3;K~dB$pJ7QISnF8Pwb>dk6UPh(xKiq%R=r>Ly z2rwon{}QL)?{$1jyZO(Xe;+guNcA_qFZhU810w!pV~*ZCeUxV6pZW~x*MP1+&mVIN I{gvDQ0CF5FGynhq delta 3488 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 UC;qr<->bO8AZZE%^zV264~c|fKmY&$ -- 2.16.6 From 3452a390f7f04f4873a47718e2d8e558e92ce03a Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Fri, 9 Nov 2018 18:03:13 +0000 Subject: [PATCH 02/16] Add support for ResourceInstanceGroup Refactor existing code and add a Junit test for the relevant method. Change-Id: I66703f1d2e93173df4676070faf66e289360609a Issue-ID: AAI-1884 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 76 ++++++++++++------ .../parser/TestArtifactGeneratorToscaParser.java | 93 +++++++++++++++++++++- src/test/resources/filter-types.properties | 2 +- 3 files changed, 141 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index e5141a5..80b75b0 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -31,7 +31,6 @@ 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; @@ -234,13 +233,14 @@ public class ArtifactGeneratorToscaParser { * @param serviceNode * @return resources for which XML Models should be generated */ - private List processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) { + List processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) { List resources = new ArrayList<>(); if (csarHelper.getNodeTemplateByName(serviceNode.getName()).getSubMappingToscaTemplate() != null) { List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode); for (Group group : serviceGroups) { if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { - resources.addAll(processInstanceGroup(resourceModel, group)); + resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), + group.getMetadata().getAllProperties(), group.getProperties())); } } } @@ -248,42 +248,56 @@ public class ArtifactGeneratorToscaParser { } /** - * Create an Instance Group Model for the supplied Service Group and relate this to the supplied resource Model. + * Create an Instance Group Model and populate it with the supplied data. * * @param resourceModel the Resource node template Model - * @param group the Service Group + * @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 processInstanceGroup(Model resourceModel, Group group) { + private List processInstanceGroup(Model resourceModel, ArrayList memberNodes, + Map metaProperties, Map properties) { List resources = new ArrayList<>(); Resource groupModel = new InstanceGroup(); - groupModel.populateModelIdentificationInformation(group.getMetadata().getAllProperties()); - groupModel.populateModelIdentificationInformation(populateStringProperties(group.getProperties())); + groupModel.populateModelIdentificationInformation(metaProperties); + groupModel.populateModelIdentificationInformation(populateStringProperties(properties)); resourceModel.addResource(groupModel); resources.add(groupModel); - List members = group.getMemberNodes(); - if (members != null && !members.isEmpty()) { - for (NodeTemplate nodeTemplate : members) { - String nodeTypeName = normaliseNodeTypeName(nodeTemplate); - Model memberModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); - memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); - if (memberModel instanceof Resource) { - log.debug("Generating grouped Resource " + nodeTypeName); - groupModel.addResource((Resource) memberModel); - resources.add((Resource) memberModel); - } else { - log.debug("Generating grouped Widget " + nodeTypeName); - groupModel.addWidget((Widget) memberModel); - } - } + if (memberNodes != null && !memberNodes.isEmpty()) { + resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel)); } return resources; } + /** + * @param memberNodes + * @param groupModel + * @return + */ + private List generateResourcesAndWidgets(final ArrayList memberNodes, + final Resource groupModel) { + List 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. @@ -300,15 +314,25 @@ public class ArtifactGeneratorToscaParser { model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); } + addRelatedModel(service, model); if (model instanceof Resource) { nodesById.put(model.getModelNameVersionId(), nodeTypeName); - service.addResource((Resource) model); - } else { - service.addWidget((Widget) model); } } } + /** + * @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. * diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index a74efaf..822cda5 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -21,11 +21,25 @@ package org.onap.aai.babel.parser; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import java.util.Properties; import org.junit.Test; +import org.mockito.Mockito; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.xml.generator.model.InstanceGroup; +import org.onap.aai.babel.xml.generator.model.Model; +import org.onap.aai.babel.xml.generator.model.Resource; +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.SubstitutionMappings; /** * Direct tests of the TOSCA parser-based Artifact Generator, to cover exceptional cases. @@ -33,7 +47,7 @@ import org.onap.sdc.toscaparser.api.NodeTemplate; public class TestArtifactGeneratorToscaParser { - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(null); + private static final String TEST_UUID = "1234"; /** * Process a dummy Node Template object for a Service. A WARNING should be logged for the missing metadata. @@ -41,16 +55,89 @@ public class TestArtifactGeneratorToscaParser { @Test public void testMissingServiceData() { List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(null); parser.processServiceTosca(null, Collections.emptyMap(), nodeTemplateList); parser.processResourceToscas(nodeTemplateList, null); } + /** + * Process a dummy Group object for a Service Resource. + */ + @Test + public void testInstanceGroups() { + final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; + Properties props = new Properties(); + props.put("AAI.instance-group-types", instanceGroupType); + WidgetConfigurationUtil.setFilterConfig(props); + + ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); + NodeTemplate serviceNodeTemplate = Mockito.mock(NodeTemplate.class); + SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); + + Mockito.when(serviceNodeTemplate.getSubMappingToscaTemplate()).thenReturn(sm); + + NodeTemplate serviceNode = buildNodeTemplate("service", "org.openecomp.resource.cr.a-collection-resource"); + Mockito.when(helper.getNodeTemplateByName(serviceNode.getName())).thenReturn(serviceNodeTemplate); + + ArrayList groups = new ArrayList<>(); + groups.add(buildGroup("group", instanceGroupType)); + Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNode)).thenReturn(groups); + + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); + Model resourceModel = new InstanceGroup(); + List resources = parser.processInstanceGroups(resourceModel, serviceNode); + + assertThat(resources.size(), is(1)); + Resource resource = resources.get(0); + assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); + } + private NodeTemplate buildNodeTemplate(String name, String type) { LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); + nodeTemplateMap.put(name, buildMap("type", type)); + nodeTemplateMap.put(type, buildNodeTemplateCustomDefs()); + return new NodeTemplate(name, nodeTemplateMap, nodeTemplateMap, null, null); + } + + private LinkedHashMap buildNodeTemplateCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("attributes", null); + customDefs.put("requirements", null); + customDefs.put("capabilities", null); + customDefs.put("artifacts", null); + return customDefs; + } + + private Group buildGroup(String name, String type) { LinkedHashMap template = new LinkedHashMap<>(); template.put("type", type); - nodeTemplateMap.put(name, template); - return new NodeTemplate(name, nodeTemplateMap, null, null, null); + template.put("metadata", new LinkedHashMap<>()); + template.put("properties", buildMap("UUID", TEST_UUID)); + LinkedHashMap customDefMap = buildMap(name, template); + customDefMap.put(type, buildGroupCustomDefs()); + return new Group(name, template, null, customDefMap); } + private LinkedHashMap buildGroupCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("members", null); + return customDefs; + } + + private LinkedHashMap buildCustomDefs() { + LinkedHashMap customDefs = new LinkedHashMap<>(); + customDefs.put("derived_from", null); + customDefs.put("metadata", null); + customDefs.put("version", null); + customDefs.put("description", null); + customDefs.put("interfaces", null); + customDefs.put("properties", buildMap("UUID", buildMap("type", "java.lang.String"))); + return customDefs; + } + + private LinkedHashMap buildMap(String key, Object value) { + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + return map; + } } diff --git a/src/test/resources/filter-types.properties b/src/test/resources/filter-types.properties index fcf139f..8577841 100644 --- a/src/test/resources/filter-types.properties +++ b/src/test/resources/filter-types.properties @@ -1 +1 @@ -AAI.instance-group-types=org.openecomp.groups.NetworkCollection,org.openecomp.groups.VfcInstanceGroup +AAI.instance-group-types=org.openecomp.groups.NetworkCollection,org.openecomp.groups.VfcInstanceGroup,org.openecomp.groups.ResourceInstanceGroup -- 2.16.6 From 36606e8fbcae1248aff9740717a666120cf9d8a0 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Thu, 22 Nov 2018 12:24:44 +0000 Subject: [PATCH 03/16] Refactor Instance Group processing Minor refactoring and reformatting of Java code using Eclipse. Add private method mergeProperties() to replace duplicated code logic for populating the Group model. Change-Id: I1360bbbf5b065149028a94392dd530af9c9153e8 Issue-ID: AAI-1884 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 888 +++++++++++---------- .../xml/generator/api/AaiArtifactGenerator.java | 10 +- .../parser/TestArtifactGeneratorToscaParser.java | 184 +++-- .../aai/babel/service/CsarToXmlConverterTest.java | 349 ++++---- 4 files changed, 732 insertions(+), 699 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index 80b75b0..ed7fc19 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -25,12 +25,14 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; 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 java.util.stream.Stream; import org.onap.aai.babel.logging.ApplicationMsgs; import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; @@ -54,431 +56,463 @@ 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. 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 = - "Cannot generate artifacts. Providing Service is missing for allotted resource %s"; - - // Metadata properties - private static final String CATEGORY = "category"; - private static final String ALLOTTED_RESOURCE = "Allotted Resource"; - private static final String SUBCATEGORY = "subcategory"; - private static final String TUNNEL_XCONNECT = "Tunnel XConnect"; - - private static final String VERSION = "version"; - - private ISdcCsarHelper csarHelper; - - /** - * Constructs using csarHelper - * - * @param csarHelper The csar helper - */ - public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { - this.csarHelper = csarHelper; - } - - /** - * Returns the artifact description - * - * @param model the artifact model - * @return the artifact model's description - */ - public static String getArtifactDescription(Model model) { - String artifactDesc = model.getModelDescription(); - if (model.getModelType().equals(ModelType.SERVICE)) { - artifactDesc = "AAI Service Model"; - } else if (model.getModelType().equals(ModelType.RESOURCE)) { - artifactDesc = "AAI Resource Model"; - } - return artifactDesc; - } - - /** - * Initialises the widget configuration. - * - * @throws IOException - */ - public static void initWidgetConfiguration() throws IOException { - log.debug("Getting Widget Configuration"); - String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); - if (configLocation != null) { - File file = new File(configLocation); - if (file.exists()) { - Properties properties = new Properties(); - properties.load(new FileInputStream(file)); - WidgetConfigurationUtil.setConfig(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_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 idTypeStore, - List 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()); - } - } - } - - /** - * Generates a Resource List using input Service Node Templates. - * - * @param serviceNodes input Service Node Templates - * @param idTypeStore ID->Type mapping - * - * @return the processed resource models - */ - public List processResourceToscas(List serviceNodes, Map idTypeStore) { - List resources = new LinkedList<>(); - for (NodeTemplate serviceNode : serviceNodes) { - if (serviceNode.getMetaData() != null) { - resources.addAll(processResourceTosca(idTypeStore, serviceNode, - csarHelper.getNodeTemplateChildren(serviceNode))); - } else { - log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, serviceNode.getName()); - } - } - return resources; - } - - /** - * @param idTypeStore ID->Type mapping - * @param serviceNode - * @param resourceNodes - * @return the processed resource models - */ - private List processResourceTosca(Map idTypeStore, NodeTemplate serviceNode, - List resourceNodes) { - List 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("Processing resource " + nodeTypeName + ": " + resourceUuId); - Map serviceMetadata = serviceNode.getMetaData().getAllProperties(); - resourceModel.populateModelIdentificationInformation(serviceMetadata); - - idTypeStore.remove(resourceModel.getModelNameVersionId()); - processResourceModels(idTypeStore, resourceModel, resourceNodes); - - 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; - } - - /** - * 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 processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) { - List resources = new ArrayList<>(); - if (csarHelper.getNodeTemplateByName(serviceNode.getName()).getSubMappingToscaTemplate() != null) { - List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode); - for (Group group : serviceGroups) { - if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { - resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), - group.getMetadata().getAllProperties(), group.getProperties())); - } - } - } - return resources; - } - - /** - * 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 processInstanceGroup(Model resourceModel, ArrayList memberNodes, - Map metaProperties, Map properties) { - List 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 generateResourcesAndWidgets(final ArrayList memberNodes, - final Resource groupModel) { - List 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 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 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); - List serviceGroups = csarHelper.getVfModulesByVf(uuid); - - // Process each VF Group - for (Group serviceGroup : serviceGroups) { - Model groupModel = Model.getModelFor(serviceGroup.getType()); - if (groupModel instanceof VfModule) { - processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); - } - } - } - - private void processVfModule(List resources, Model model, Group groupDefinition, NodeTemplate serviceNode, - VfModule groupModel) { - // Populate group with metadata properties - groupModel.populateModelIdentificationInformation(groupDefinition.getMetadata().getAllProperties()); - // Populate group with non-metadata properties - Map groupProperties = groupDefinition.getProperties(); - Map properties = populateStringProperties(groupProperties); - groupModel.populateModelIdentificationInformation(properties); - processVfModuleGroup(resources, model, groupDefinition, serviceNode, groupModel); - } - - private void processVfModuleGroup(List resources, Model model, Group groupDefinition, - NodeTemplate serviceNode, VfModule groupModel) { - // Get names of the members of the service group - List members = csarHelper.getMembersOfVfModule(serviceNode, groupDefinition); - if (members != null && !members.isEmpty()) { - List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); - groupModel.setMembers(memberNames); - for (NodeTemplate member : members) { - processGroupMembers(groupModel, member); - } - } - - model.addResource(groupModel); // Added group (VfModule) to the (VF) model - // Check if we have already encountered the same VfModule across all the artifacts - if (!resources.contains(groupModel)) { - resources.add(groupModel); - } - } - - private void processGroupMembers(Model group, NodeTemplate member) { - Model resourceNode; - // L3-network inside vf-module to be generated as Widget a special handling. - if (member.getType().contains("org.openecomp.resource.vl")) { - resourceNode = new L3NetworkWidget(); - } else { - resourceNode = Model.getModelFor(member.getType()); - } - if (resourceNode != null && !(resourceNode instanceof Resource)) { - Widget widget = (Widget) resourceNode; - widget.addKey(member.getName()); - // Add the widget element encountered to the Group model - group.addWidget(widget); - } - } - - 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.")) { - nodeTypeName = "org.openecomp.resource.vf.allottedResource"; - } - if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { - nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; - } - } - return nodeTypeName; - } - - private boolean hasAllottedResource(Map metadata) { - return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); - } - - private boolean hasSubCategoryTunnelXConnect(Map metadata) { - return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); - } - - /** - * Create a Map of property name against String property value from the input Map - * - * @param inputMap The input Map - * @return Map of property name against String property value - */ - private Map populateStringProperties(Map inputMap) { - return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString())); - } - - private void processResourceModels(Map idTypeStore, Model resourceModel, - List resourceNodes) { - boolean foundProvidingService = false; - - for (NodeTemplate resourceNodeTemplate : resourceNodes) { - 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 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; - } - - private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) { - Map 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, resourceModel.getModelId())); - } - Map properties = populateStringProperties(nodeProperties); - properties.put(VERSION, "1.0"); - resourceNode.populateModelIdentificationInformation(properties); - resourceModel.addResource((Resource) resourceNode); - } - + 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. 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 = "Cannot generate artifacts. Providing Service is missing for allotted resource %s"; + + // Metadata properties + private static final String CATEGORY = "category"; + private static final String ALLOTTED_RESOURCE = "Allotted Resource"; + private static final String SUBCATEGORY = "subcategory"; + private static final String TUNNEL_XCONNECT = "Tunnel XConnect"; + + private static final String VERSION = "version"; + + private ISdcCsarHelper csarHelper; + + /** + * Constructs using csarHelper + * + * @param csarHelper + * The csar helper + */ + public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { + this.csarHelper = csarHelper; + } + + /** + * Returns the artifact description + * + * @param model + * the artifact model + * @return the artifact model's description + */ + public static String getArtifactDescription(Model model) { + String artifactDesc = model.getModelDescription(); + if (model.getModelType().equals(ModelType.SERVICE)) { + artifactDesc = "AAI Service Model"; + } else if (model.getModelType().equals(ModelType.RESOURCE)) { + artifactDesc = "AAI Resource Model"; + } + return artifactDesc; + } + + /** + * Initialises the widget configuration. + * + * @throws IOException + */ + public static void initWidgetConfiguration() throws IOException { + log.debug("Getting Widget Configuration"); + String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + if (configLocation != null) { + File file = new File(configLocation); + if (file.exists()) { + Properties properties = new Properties(); + properties.load(new FileInputStream(file)); + WidgetConfigurationUtil.setConfig(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_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 idTypeStore, + List 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()); + } + } + } + + /** + * Generates a Resource List using input Service Node Templates. + * + * @param serviceNodeTemplates + * input Service Node Templates + * @param idTypeStore + * ID->Type mapping + * + * @return the processed resource models + */ + public List processResourceToscas(List serviceNodeTemplates, + Map idTypeStore) { + List resources = new LinkedList<>(); + for (NodeTemplate serviceNodeTemplate : serviceNodeTemplates) { + if (serviceNodeTemplate.getMetaData() != null) { + resources.addAll(processResourceTosca(idTypeStore, serviceNodeTemplate, + csarHelper.getNodeTemplateChildren(serviceNodeTemplate))); + } else { + log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, serviceNodeTemplate.getName()); + } + } + return resources; + } + + /** + * @param idTypeStore + * ID->Type mapping + * @param serviceNodeTemplate + * @param resourceNodeTemplates + * the (non-VNF) substituted node templates + * @return the processed resource models + */ + private List processResourceTosca(Map idTypeStore, NodeTemplate serviceNodeTemplate, + List resourceNodeTemplates) { + List resources = new LinkedList<>(); + String resourceUuId = serviceNodeTemplate.getMetaData().getValue("UUID"); + String nodeTypeName = idTypeStore.get(resourceUuId); + if (nodeTypeName != null) { + Model resourceModel = Model.getModelFor(nodeTypeName, serviceNodeTemplate.getMetaData().getValue("type")); + + log.debug("Processing resource " + nodeTypeName + ": " + resourceUuId); + Map serviceMetadata = serviceNodeTemplate.getMetaData().getAllProperties(); + resourceModel.populateModelIdentificationInformation(serviceMetadata); + + idTypeStore.remove(resourceModel.getModelNameVersionId()); + processResourceModels(idTypeStore, resourceModel, resourceNodeTemplates); + + if (csarHelper.getServiceVfList() != null) { + processVfModules(resources, resourceModel, serviceNodeTemplate); + } + + if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) { + resourceModel.addWidget(new TunnelXconnectWidget()); + } + + resources.addAll(processInstanceGroups(resourceModel, serviceNodeTemplate)); + resources.add((Resource) resourceModel); + } + + return resources; + } + + /** + * 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 processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) { + List resources = new ArrayList<>(); + if (serviceNode.getSubMappingToscaTemplate() != null) { + List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode); + for (Group group : serviceGroups) { + if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { + resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), + group.getMetadata().getAllProperties(), group.getProperties())); + } + } + } + return resources; + } + + /** + * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are + * duplicate keys then the TOSCA Property value takes precedence. + * + * @param stringProps + * initial Map of String property values (e.g. from the TOSCA YAML metadata section) + * @param toscaProps + * Map of TOSCA Property Type Object values to merge in (or overwrite) + * @return a Map of the property values converted to String + */ + private Map mergeProperties(Map stringProps, Map toscaProps) { + Map props = new HashMap<>(stringProps); + toscaProps.forEach((key, toscaProp) -> props.put(key, + toscaProp.getValue() == null ? "" : toscaProp.getValue().toString())); + return props; + } + + /** + * 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 processInstanceGroup(Model resourceModel, ArrayList memberNodes, + Map metaProperties, Map properties) { + Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties)); + resourceModel.addResource(groupModel); + List resources = Stream.of(groupModel).collect(Collectors.toList()); + + if (memberNodes != null && !memberNodes.isEmpty()) { + resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel)); + } + + return resources; + } + + private Resource createInstanceGroupModel(Map properties) { + Resource groupModel = new InstanceGroup(); + groupModel.populateModelIdentificationInformation(properties); + return groupModel; + } + + /** + * @param memberNodes + * @param groupModel + * @return + */ + private List generateResourcesAndWidgets(final ArrayList memberNodes, + final Resource groupModel) { + List 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 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 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); + List serviceGroups = csarHelper.getVfModulesByVf(uuid); + + // Process each VF Group + for (Group serviceGroup : serviceGroups) { + Model groupModel = Model.getModelFor(serviceGroup.getType()); + if (groupModel instanceof VfModule) { + processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); + } + } + } + + private void processVfModule(List resources, Model model, Group groupDefinition, NodeTemplate serviceNode, + VfModule groupModel) { + groupModel.populateModelIdentificationInformation( + mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); + processVfModuleGroup(resources, model, groupDefinition, serviceNode, groupModel); + } + + private void processVfModuleGroup(List resources, Model model, Group groupDefinition, + NodeTemplate serviceNode, VfModule groupModel) { + // Get names of the members of the service group + List members = csarHelper.getMembersOfVfModule(serviceNode, groupDefinition); + if (members != null && !members.isEmpty()) { + List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); + groupModel.setMembers(memberNames); + for (NodeTemplate member : members) { + processGroupMembers(groupModel, member); + } + } + + model.addResource(groupModel); // Added group (VfModule) to the (VF) model + // Check if we have already encountered the same VfModule across all the artifacts + if (!resources.contains(groupModel)) { + resources.add(groupModel); + } + } + + private void processGroupMembers(Model group, NodeTemplate member) { + Model resourceNode; + // L3-network inside vf-module to be generated as Widget a special handling. + if (member.getType().contains("org.openecomp.resource.vl")) { + resourceNode = new L3NetworkWidget(); + } else { + resourceNode = Model.getModelFor(member.getType()); + } + if (resourceNode != null && !(resourceNode instanceof Resource)) { + Widget widget = (Widget) resourceNode; + widget.addKey(member.getName()); + // Add the widget element encountered to the Group model + group.addWidget(widget); + } + } + + 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.")) { + nodeTypeName = "org.openecomp.resource.vf.allottedResource"; + } + if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { + nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; + } + } + return nodeTypeName; + } + + private boolean hasAllottedResource(Map metadata) { + return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); + } + + private boolean hasSubCategoryTunnelXConnect(Map metadata) { + return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); + } + + /** + * Create a Map of property name against String property value from the input Map + * + * @param inputMap + * The input Map + * @return Map of property name against String property value + */ + private Map populateStringProperties(Map inputMap) { + return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString())); + } + + /** + * @param idTypeStore + * @param resourceModel + * @param resourceNodeTemplates + */ + private void processResourceModels(Map idTypeStore, Model resourceModel, + List resourceNodeTemplates) { + boolean foundProvidingService = false; + + for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { + 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 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; + } + + private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) { + Map 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, resourceModel.getModelId())); + } + Map properties = populateStringProperties(nodeProperties); + properties.put(VERSION, "1.0"); + resourceNode.populateModelIdentificationInformation(properties); + resourceModel.addResource((Resource) resourceNode); + } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index 19a08f2..67c0b2f 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -102,8 +102,8 @@ public class AaiArtifactGenerator implements ArtifactGenerator { * @return the generated Artifacts */ private GenerationData generateService(final String serviceVersion, ISdcCsarHelper csarHelper) { - List serviceNodes = csarHelper.getServiceNodeTemplates(); - if (serviceNodes == null) { + List serviceNodeTemplates = csarHelper.getServiceNodeTemplates(); + if (serviceNodeTemplates == null) { throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); } @@ -115,12 +115,12 @@ public class AaiArtifactGenerator implements ArtifactGenerator { Map idTypeStore = new HashMap<>(); ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); - if (!serviceNodes.isEmpty()) { - parser.processServiceTosca(serviceModel, idTypeStore, serviceNodes); + if (!serviceNodeTemplates.isEmpty()) { + parser.processServiceTosca(serviceModel, idTypeStore, serviceNodeTemplates); } // Process the resource TOSCA files - List resources = parser.processResourceToscas(serviceNodes, idTypeStore); + List resources = parser.processResourceToscas(serviceNodeTemplates, idTypeStore); MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); String aaiServiceModel = modelGenerator.generateModelFor(serviceModel); diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index 822cda5..6cf6d31 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -34,7 +34,6 @@ import org.junit.Test; import org.mockito.Mockito; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; import org.onap.aai.babel.xml.generator.model.InstanceGroup; -import org.onap.aai.babel.xml.generator.model.Model; import org.onap.aai.babel.xml.generator.model.Resource; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.toscaparser.api.Group; @@ -47,97 +46,94 @@ import org.onap.sdc.toscaparser.api.SubstitutionMappings; public class TestArtifactGeneratorToscaParser { - private static final String TEST_UUID = "1234"; - - /** - * Process a dummy Node Template object for a Service. A WARNING should be logged for the missing metadata. - */ - @Test - public void testMissingServiceData() { - List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(null); - parser.processServiceTosca(null, Collections.emptyMap(), nodeTemplateList); - parser.processResourceToscas(nodeTemplateList, null); - } - - /** - * Process a dummy Group object for a Service Resource. - */ - @Test - public void testInstanceGroups() { - final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; - Properties props = new Properties(); - props.put("AAI.instance-group-types", instanceGroupType); - WidgetConfigurationUtil.setFilterConfig(props); - - ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); - NodeTemplate serviceNodeTemplate = Mockito.mock(NodeTemplate.class); - SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); - - Mockito.when(serviceNodeTemplate.getSubMappingToscaTemplate()).thenReturn(sm); - - NodeTemplate serviceNode = buildNodeTemplate("service", "org.openecomp.resource.cr.a-collection-resource"); - Mockito.when(helper.getNodeTemplateByName(serviceNode.getName())).thenReturn(serviceNodeTemplate); - - ArrayList groups = new ArrayList<>(); - groups.add(buildGroup("group", instanceGroupType)); - Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNode)).thenReturn(groups); - - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); - Model resourceModel = new InstanceGroup(); - List resources = parser.processInstanceGroups(resourceModel, serviceNode); - - assertThat(resources.size(), is(1)); - Resource resource = resources.get(0); - assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); - } - - private NodeTemplate buildNodeTemplate(String name, String type) { - LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); - nodeTemplateMap.put(name, buildMap("type", type)); - nodeTemplateMap.put(type, buildNodeTemplateCustomDefs()); - return new NodeTemplate(name, nodeTemplateMap, nodeTemplateMap, null, null); - } - - private LinkedHashMap buildNodeTemplateCustomDefs() { - LinkedHashMap customDefs = buildCustomDefs(); - customDefs.put("attributes", null); - customDefs.put("requirements", null); - customDefs.put("capabilities", null); - customDefs.put("artifacts", null); - return customDefs; - } - - private Group buildGroup(String name, String type) { - LinkedHashMap template = new LinkedHashMap<>(); - template.put("type", type); - template.put("metadata", new LinkedHashMap<>()); - template.put("properties", buildMap("UUID", TEST_UUID)); - LinkedHashMap customDefMap = buildMap(name, template); - customDefMap.put(type, buildGroupCustomDefs()); - return new Group(name, template, null, customDefMap); - } - - private LinkedHashMap buildGroupCustomDefs() { - LinkedHashMap customDefs = buildCustomDefs(); - customDefs.put("members", null); - return customDefs; - } - - private LinkedHashMap buildCustomDefs() { - LinkedHashMap customDefs = new LinkedHashMap<>(); - customDefs.put("derived_from", null); - customDefs.put("metadata", null); - customDefs.put("version", null); - customDefs.put("description", null); - customDefs.put("interfaces", null); - customDefs.put("properties", buildMap("UUID", buildMap("type", "java.lang.String"))); - return customDefs; - } - - private LinkedHashMap buildMap(String key, Object value) { - LinkedHashMap map = new LinkedHashMap<>(); - map.put(key, value); - return map; - } + private static final String TEST_UUID = "1234"; + + /** + * Process a dummy Node Template object for a Service. A WARNING should be logged for the missing metadata. + */ + @Test + public void testMissingServiceData() { + List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(null); + parser.processServiceTosca(null, Collections.emptyMap(), nodeTemplateList); + parser.processResourceToscas(nodeTemplateList, null); + } + + /** + * Process a dummy Group object for a Service Resource. + */ + @Test + public void testInstanceGroups() { + final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; + Properties props = new Properties(); + props.put("AAI.instance-group-types", instanceGroupType); + WidgetConfigurationUtil.setFilterConfig(props); + + ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); + SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); + + NodeTemplate serviceNode = buildNodeTemplate("service", "org.openecomp.resource.cr.a-collection-resource"); + serviceNode.setSubMappingToscaTemplate(sm); + Mockito.when(helper.getNodeTemplateByName(serviceNode.getName())).thenReturn(serviceNode); + + ArrayList groups = new ArrayList<>(); + groups.add(buildGroup("group", instanceGroupType)); + Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNode)).thenReturn(groups); + + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); + List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNode); + + assertThat(resources.size(), is(1)); + Resource resource = resources.get(0); + assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); + } + + private NodeTemplate buildNodeTemplate(String name, String type) { + LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); + nodeTemplateMap.put(name, buildMap("type", type)); + nodeTemplateMap.put(type, buildNodeTemplateCustomDefs()); + return new NodeTemplate(name, nodeTemplateMap, nodeTemplateMap, null, null); + } + + private LinkedHashMap buildNodeTemplateCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("attributes", null); + customDefs.put("requirements", null); + customDefs.put("capabilities", null); + customDefs.put("artifacts", null); + return customDefs; + } + + private Group buildGroup(String name, String type) { + LinkedHashMap template = new LinkedHashMap<>(); + template.put("type", type); + template.put("metadata", new LinkedHashMap<>()); + template.put("properties", buildMap("UUID", TEST_UUID)); + LinkedHashMap customDefMap = buildMap(name, template); + customDefMap.put(type, buildGroupCustomDefs()); + return new Group(name, template, null, customDefMap); + } + + private LinkedHashMap buildGroupCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("members", null); + return customDefs; + } + + private LinkedHashMap buildCustomDefs() { + LinkedHashMap customDefs = new LinkedHashMap<>(); + customDefs.put("derived_from", null); + customDefs.put("metadata", null); + customDefs.put("version", null); + customDefs.put("description", null); + customDefs.put("interfaces", null); + customDefs.put("properties", buildMap("UUID", buildMap("type", "java.lang.String"))); + return customDefs; + } + + private LinkedHashMap buildMap(String key, Object value) { + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + return map; + } } diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 8d10412..297bb4d 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -53,177 +53,180 @@ import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException; */ public class CsarToXmlConverterTest { - private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; - private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; - - private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; - private static final String SERVICE_VERSION = "1.0"; - - static { - if (System.getProperty("APP_HOME") == null) { - System.setProperty("APP_HOME", "."); - } - } - - // The class to be tested. - private CsarToXmlConverter converter; - - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Before - public void setup() { - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); - - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); - - converter = new CsarToXmlConverter(); - } - - @After - public void tearDown() { - converter = null; - } - - @Test(expected = NullPointerException.class) - public void testNullArtifactSupplied() throws CsarConverterException { - converter.generateXmlFromCsar(null, null, null); - } - - @Test(expected = NullPointerException.class) - public void testMissingName() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), null, null); - } - - @Test(expected = NullPointerException.class) - public void testMissingVersion() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), INCORRECT_CSAR_NAME, null); - } - - @Test(expected = CsarConverterException.class) - public void testNoPayloadExists() throws CsarConverterException { - converter.generateXmlFromCsar(new byte[0], INCORRECT_CSAR_NAME, SERVICE_VERSION); - } - - @Test(expected = CsarConverterException.class) - public void testCsarFileHasNoYmlFiles() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.NO_YAML_FILES.getContent(), CsarTest.NO_YAML_FILES.getName(), - SERVICE_VERSION); - } - - /** - * Test that an Exception is thrown when the Artifact Generator properties are not present. - * - * @throws CsarConverterException if there is an error either extracting the YAML files or generating XML artifacts - * @throws IOException if an I/O exception occurs loading the test CSAR file - * @throws IOException - * @throws XmlArtifactGenerationException - * @throws CsarConverterException - */ - @Test - public void testArtifactGeneratorConfigMissing() throws CsarConverterException, IOException { - exception.expect(CsarConverterException.class); - exception.expectMessage("Cannot generate artifacts. System property artifactgenerator.config not configured"); - - // Unset the required system property - System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), - SERVICE_VERSION); - } - - /** - * Test that an Exception is thrown when the Artifact Generator's Group Filter properties are not present. - * - * @throws IOException - * @throws XmlArtifactGenerationException - * @throws CsarConverterException - */ - @Test - public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() - throws IOException, XmlArtifactGenerationException, CsarConverterException { - exception.expect(CsarConverterException.class); - exception.expectMessage("Cannot generate artifacts. System property groupfilter.config not configured"); - - // Unset the required system property - System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE); - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), - SERVICE_VERSION); - } - - @Test - public void testServiceMetadataMissing() - throws IOException, XmlArtifactGenerationException, CsarConverterException { - converter.generateXmlFromCsar(CsarTest.MISSING_METADATA_CSAR.getContent(), - CsarTest.MISSING_METADATA_CSAR.getName(), SERVICE_VERSION); - } - - @Test - public void generateXmlFromSdWanCsar() throws IOException, CsarConverterException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-SD-WAN-Service-Test-service-1.0.xml"); - filesToLoad.add("AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml"); - filesToLoad.add("AAI-Tunnel_XConnTest-resource-2.0.xml"); - filesToLoad.add("AAI-SD-WAN-Test-VSP-resource-1.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SD_WAN_CSAR_FILE); - } - - @Test - public void generateXmlFromNetworkCollectionCsar() throws IOException, CsarConverterException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-TEST SVC_1-service-1.0.xml"); - filesToLoad.add("AAI-TEST CR_1-resource-7.0.xml"); - filesToLoad.add("AAI-testcr_1..NetworkCollection..0-resource-1.xml"); - filesToLoad.add("AAI-ExtVL-resource-40.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.NETWORK_COLLECTION_CSAR_FILE); - } - - @Test - public void generatePortMirrorConfigurationModel() - throws CsarConverterException, IOException, XmlArtifactGenerationException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-Port Mirror_Test-service-1.0.xml"); - filesToLoad.add("AAI-Port Mirroring Configuration-resource-35.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.PORT_MIRROR_CSAR); - } - - public Matcher matches(final String expected) { - return new BaseMatcher() { - protected String theExpected = expected; - - @Override - public boolean matches(Object item) { - return new ArtifactTestUtils().compareXmlStrings((String) item, theExpected); - } - - @Override - public void describeTo(Description description) { - description.appendText(theExpected.toString()); - } - }; - } - - private Map createExpectedXmlFiles(List filesToLoad) throws IOException { - Map xmlMap = new HashMap<>(); - for (String filename : filesToLoad) { - xmlMap.put(filename, new ArtifactTestUtils().loadResourceAsString("generatedXml/" + filename)); - } - return xmlMap; - } - - private void assertThatGeneratedFilesMatchExpected(Map expectedXmlFiles, CsarTest csarFile) - throws CsarConverterException, IOException { - List generatedArtifacts = - converter.generateXmlFromCsar(csarFile.getContent(), csarFile.getName(), SERVICE_VERSION); - assertThat("Incorrect number of files generated", // - generatedArtifacts.size(), is(equalTo(expectedXmlFiles.size()))); - for (BabelArtifact generated : generatedArtifacts) { - String fileName = generated.getName(); - String expectedXml = expectedXmlFiles.get(fileName); - assertThat("Missing expected content for " + generated.getName(), expectedXml, is(not(nullValue()))); - assertThat("The content of " + generated.getName() + " must match the expected content", - generated.getPayload(), matches(expectedXml)); - } - } + private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; + private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; + + private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; + private static final String SERVICE_VERSION = "1.0"; + + static { + if (System.getProperty("APP_HOME") == null) { + System.setProperty("APP_HOME", "."); + } + } + + // The class to be tested. + private CsarToXmlConverter converter; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Before + public void setup() { + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, + new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); + + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, + new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); + + converter = new CsarToXmlConverter(); + } + + @After + public void tearDown() { + converter = null; + } + + @Test(expected = NullPointerException.class) + public void testNullArtifactSupplied() throws CsarConverterException { + converter.generateXmlFromCsar(null, null, null); + } + + @Test(expected = NullPointerException.class) + public void testMissingName() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), null, null); + } + + @Test(expected = NullPointerException.class) + public void testMissingVersion() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), INCORRECT_CSAR_NAME, null); + } + + @Test(expected = CsarConverterException.class) + public void testNoPayloadExists() throws CsarConverterException { + converter.generateXmlFromCsar(new byte[0], INCORRECT_CSAR_NAME, SERVICE_VERSION); + } + + @Test(expected = CsarConverterException.class) + public void testCsarFileHasNoYmlFiles() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.NO_YAML_FILES.getContent(), CsarTest.NO_YAML_FILES.getName(), + SERVICE_VERSION); + } + + /** + * Test that an Exception is thrown when the Artifact Generator properties are not present. + * + * @throws CsarConverterException + * if there is an error either extracting the YAML files or generating XML artifacts + * @throws IOException + * if an I/O exception occurs loading the test CSAR file + * @throws IOException + * @throws XmlArtifactGenerationException + * @throws CsarConverterException + */ + @Test + public void testArtifactGeneratorConfigMissing() throws CsarConverterException, IOException { + exception.expect(CsarConverterException.class); + exception.expectMessage("Cannot generate artifacts. System property artifactgenerator.config not configured"); + + // Unset the required system property + System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), + SERVICE_VERSION); + } + + /** + * Test that an Exception is thrown when the Artifact Generator's Group Filter properties are not present. + * + * @throws IOException + * @throws XmlArtifactGenerationException + * @throws CsarConverterException + */ + @Test + public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() + throws IOException, XmlArtifactGenerationException, CsarConverterException { + exception.expect(CsarConverterException.class); + exception.expectMessage("Cannot generate artifacts. System property groupfilter.config not configured"); + + // Unset the required system property + System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE); + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), + SERVICE_VERSION); + } + + @Test + public void testServiceMetadataMissing() + throws IOException, XmlArtifactGenerationException, CsarConverterException { + converter.generateXmlFromCsar(CsarTest.MISSING_METADATA_CSAR.getContent(), + CsarTest.MISSING_METADATA_CSAR.getName(), SERVICE_VERSION); + } + + @Test + public void generateXmlFromSdWanCsar() throws IOException, CsarConverterException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-SD-WAN-Service-Test-service-1.0.xml"); + filesToLoad.add("AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml"); + filesToLoad.add("AAI-Tunnel_XConnTest-resource-2.0.xml"); + filesToLoad.add("AAI-SD-WAN-Test-VSP-resource-1.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SD_WAN_CSAR_FILE); + } + + @Test + public void generateXmlFromNetworkCollectionCsar() throws IOException, CsarConverterException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-TEST SVC_1-service-1.0.xml"); + filesToLoad.add("AAI-TEST CR_1-resource-7.0.xml"); + filesToLoad.add("AAI-testcr_1..NetworkCollection..0-resource-1.xml"); + filesToLoad.add("AAI-ExtVL-resource-40.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), + CsarTest.NETWORK_COLLECTION_CSAR_FILE); + } + + @Test + public void generatePortMirrorConfigurationModel() + throws CsarConverterException, IOException, XmlArtifactGenerationException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-Port Mirror_Test-service-1.0.xml"); + filesToLoad.add("AAI-Port Mirroring Configuration-resource-35.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.PORT_MIRROR_CSAR); + } + + public Matcher matches(final String expected) { + return new BaseMatcher() { + protected String theExpected = expected; + + @Override + public boolean matches(Object item) { + return new ArtifactTestUtils().compareXmlStrings((String) item, theExpected); + } + + @Override + public void describeTo(Description description) { + description.appendText(theExpected.toString()); + } + }; + } + + private Map createExpectedXmlFiles(List filesToLoad) throws IOException { + Map xmlMap = new HashMap<>(); + for (String filename : filesToLoad) { + xmlMap.put(filename, new ArtifactTestUtils().loadResourceAsString("generatedXml/" + filename)); + } + return xmlMap; + } + + private void assertThatGeneratedFilesMatchExpected(Map expectedXmlFiles, CsarTest csarFile) + throws CsarConverterException, IOException { + List generatedArtifacts = converter.generateXmlFromCsar(csarFile.getContent(), + csarFile.getName(), SERVICE_VERSION); + assertThat("Incorrect number of files generated", // + generatedArtifacts.size(), is(equalTo(expectedXmlFiles.size()))); + for (BabelArtifact generated : generatedArtifacts) { + String fileName = generated.getName(); + String expectedXml = expectedXmlFiles.get(fileName); + assertThat("Missing expected content for " + generated.getName(), expectedXml, is(not(nullValue()))); + assertThat("The content of " + generated.getName() + " must match the expected content", + generated.getPayload(), matches(expectedXml)); + } + } } -- 2.16.6 From 91d6d53930576dba11a92d25e20795c1ba1f8817 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Tue, 27 Nov 2018 11:49:58 +0000 Subject: [PATCH 04/16] Refactor model generation algorithm Reorganize the Service and Resource Model generation code for readability. Resort methods to put public before private. Rename generateService() to generateAllArtifacts() Reimplement this method such that the NodeTemplate list is iterated once only. This is intended to simplify future changes to the code. Change-Id: Ie0c6003eab99f42945747c6d79827881e05afc87 Issue-ID: AAI-1884 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 323 +++++-------- .../xml/generator/api/AaiArtifactGenerator.java | 526 ++++++++++++--------- .../aai/babel/xml/generator/model/Resource.java | 51 +- .../parser/TestArtifactGeneratorToscaParser.java | 47 +- 4 files changed, 466 insertions(+), 481 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index ed7fc19..827e552 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -26,14 +26,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -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 java.util.stream.Stream; -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; @@ -42,8 +41,6 @@ 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; import org.onap.aai.babel.xml.generator.model.Resource; -import org.onap.aai.babel.xml.generator.model.Service; -import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget; import org.onap.aai.babel.xml.generator.model.VfModule; import org.onap.aai.babel.xml.generator.model.Widget; import org.onap.aai.babel.xml.generator.types.ModelType; @@ -149,102 +146,17 @@ public class ArtifactGeneratorToscaParser { } } - /** - * 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 idTypeStore, - List 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()); - } - } - } - - /** - * Generates a Resource List using input Service Node Templates. - * - * @param serviceNodeTemplates - * input Service Node Templates - * @param idTypeStore - * ID->Type mapping - * - * @return the processed resource models - */ - public List processResourceToscas(List serviceNodeTemplates, - Map idTypeStore) { - List resources = new LinkedList<>(); - for (NodeTemplate serviceNodeTemplate : serviceNodeTemplates) { - if (serviceNodeTemplate.getMetaData() != null) { - resources.addAll(processResourceTosca(idTypeStore, serviceNodeTemplate, - csarHelper.getNodeTemplateChildren(serviceNodeTemplate))); - } else { - log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, serviceNodeTemplate.getName()); - } - } - return resources; - } - - /** - * @param idTypeStore - * ID->Type mapping - * @param serviceNodeTemplate - * @param resourceNodeTemplates - * the (non-VNF) substituted node templates - * @return the processed resource models - */ - private List processResourceTosca(Map idTypeStore, NodeTemplate serviceNodeTemplate, - List resourceNodeTemplates) { - List resources = new LinkedList<>(); - String resourceUuId = serviceNodeTemplate.getMetaData().getValue("UUID"); - String nodeTypeName = idTypeStore.get(resourceUuId); - if (nodeTypeName != null) { - Model resourceModel = Model.getModelFor(nodeTypeName, serviceNodeTemplate.getMetaData().getValue("type")); - - log.debug("Processing resource " + nodeTypeName + ": " + resourceUuId); - Map serviceMetadata = serviceNodeTemplate.getMetaData().getAllProperties(); - resourceModel.populateModelIdentificationInformation(serviceMetadata); - - idTypeStore.remove(resourceModel.getModelNameVersionId()); - processResourceModels(idTypeStore, resourceModel, resourceNodeTemplates); - - if (csarHelper.getServiceVfList() != null) { - processVfModules(resources, resourceModel, serviceNodeTemplate); - } - - if (hasSubCategoryTunnelXConnect(serviceMetadata) && hasAllottedResource(serviceMetadata)) { - resourceModel.addWidget(new TunnelXconnectWidget()); - } - - resources.addAll(processInstanceGroups(resourceModel, serviceNodeTemplate)); - resources.add((Resource) resourceModel); - } - - return resources; - } - /** * Process groups for this service node, according to the defined filter. * * @param resourceModel - * @param serviceNode + * @param serviceNodeTemplate * @return resources for which XML Models should be generated */ - List processInstanceGroups(Model resourceModel, NodeTemplate serviceNode) { + public List processInstanceGroups(Model resourceModel, NodeTemplate serviceNodeTemplate) { List resources = new ArrayList<>(); - if (serviceNode.getSubMappingToscaTemplate() != null) { - List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNode); + if (serviceNodeTemplate.getSubMappingToscaTemplate() != null) { + List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate); for (Group group : serviceGroups) { if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), @@ -265,13 +177,96 @@ public class ArtifactGeneratorToscaParser { * Map of TOSCA Property Type Object values to merge in (or overwrite) * @return a Map of the property values converted to String */ - private Map mergeProperties(Map stringProps, Map toscaProps) { + public Map mergeProperties(Map stringProps, Map toscaProps) { Map props = new HashMap<>(stringProps); toscaProps.forEach((key, toscaProp) -> props.put(key, toscaProp.getValue() == null ? "" : toscaProp.getValue().toString())); return props; } + public Resource createInstanceGroupModel(Map properties) { + Resource groupModel = new InstanceGroup(); + groupModel.populateModelIdentificationInformation(properties); + return groupModel; + } + + /** + * @param model + * @param relation + */ + public void addRelatedModel(final Model model, final Model relation) { + if (relation instanceof Resource) { + model.addResource((Resource) relation); + } else { + model.addWidget((Widget) relation); + } + } + + public 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.")) { + nodeTypeName = "org.openecomp.resource.vf.allottedResource"; + } + if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { + nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; + } + } + return nodeTypeName; + } + + public boolean hasAllottedResource(Map metadata) { + return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); + } + + public boolean hasSubCategoryTunnelXConnect(Map metadata) { + return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); + } + + /** + * Process TOSCA Group information for VF Modules. + * + * @param resources + * @param model + * @param serviceNode + */ + public void processVfModules(List 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); + List serviceGroups = csarHelper.getVfModulesByVf(uuid); + + // Process each VF Group + for (Group serviceGroup : serviceGroups) { + Model groupModel = Model.getModelFor(serviceGroup.getType()); + if (groupModel instanceof VfModule) { + processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); + } + } + } + + /** + * @param resourceModel + * @param resourceNodeTemplates + */ + public void processResourceModels(Model resourceModel, List resourceNodeTemplates) { + boolean foundProvidingService = false; + + for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { + 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(resourceModel, resourceNodeTemplate, metaData, resourceNode); + } + + if (resourceModel instanceof AllotedResource && !foundProvidingService) { + final String modelInvariantId = resourceModel.getModelId(); + throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, + modelInvariantId == null ? "" : modelInvariantId)); + } + } + /** * Create an Instance Group Model and populate it with the supplied data. * @@ -298,12 +293,6 @@ public class ArtifactGeneratorToscaParser { return resources; } - private Resource createInstanceGroupModel(Map properties) { - Resource groupModel = new InstanceGroup(); - groupModel.populateModelIdentificationInformation(properties); - return groupModel; - } - /** * @param memberNodes * @param groupModel @@ -328,89 +317,29 @@ public class ArtifactGeneratorToscaParser { 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 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); - } - } + private void processVfModule(List resources, Model vfModel, Group groupDefinition, + NodeTemplate serviceNode, VfModule groupModel) { + groupModel.populateModelIdentificationInformation( + mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); - /** - * Process TOSCA Group information for VF Modules. - * - * @param resources - * @param model - * @param serviceNode - */ - private void processVfModules(List 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); - List serviceGroups = csarHelper.getVfModulesByVf(uuid); + processVfModuleGroup(groupModel, csarHelper.getMembersOfVfModule(serviceNode, groupDefinition)); - // Process each VF Group - for (Group serviceGroup : serviceGroups) { - Model groupModel = Model.getModelFor(serviceGroup.getType()); - if (groupModel instanceof VfModule) { - processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); - } + vfModel.addResource(groupModel); // Add group (VfModule) to the (VF) model + // Check if we have already encountered the same VfModule across all the artifacts + if (!resources.contains(groupModel)) { + resources.add(groupModel); } } - private void processVfModule(List resources, Model model, Group groupDefinition, NodeTemplate serviceNode, - VfModule groupModel) { - groupModel.populateModelIdentificationInformation( - mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); - processVfModuleGroup(resources, model, groupDefinition, serviceNode, groupModel); - } - - private void processVfModuleGroup(List resources, Model model, Group groupDefinition, - NodeTemplate serviceNode, VfModule groupModel) { - // Get names of the members of the service group - List members = csarHelper.getMembersOfVfModule(serviceNode, groupDefinition); + private void processVfModuleGroup(VfModule groupModel, List members) { if (members != null && !members.isEmpty()) { + // Get names of the members of the service group List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); groupModel.setMembers(memberNames); for (NodeTemplate member : members) { processGroupMembers(groupModel, member); } } - - model.addResource(groupModel); // Added group (VfModule) to the (VF) model - // Check if we have already encountered the same VfModule across all the artifacts - if (!resources.contains(groupModel)) { - resources.add(groupModel); - } } private void processGroupMembers(Model group, NodeTemplate member) { @@ -429,28 +358,6 @@ public class ArtifactGeneratorToscaParser { } } - 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.")) { - nodeTypeName = "org.openecomp.resource.vf.allottedResource"; - } - if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { - nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; - } - } - return nodeTypeName; - } - - private boolean hasAllottedResource(Map metadata) { - return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); - } - - private boolean hasSubCategoryTunnelXConnect(Map metadata) { - return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); - } - /** * Create a Map of property name against String property value from the input Map * @@ -464,31 +371,14 @@ public class ArtifactGeneratorToscaParser { } /** - * @param idTypeStore * @param resourceModel - * @param resourceNodeTemplates + * @param resourceNodeTemplate + * @param metaData + * @param resourceNode + * @return */ - private void processResourceModels(Map idTypeStore, Model resourceModel, - List resourceNodeTemplates) { - boolean foundProvidingService = false; - - for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { - 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 idTypeStore, Model resourceModel, - NodeTemplate resourceNodeTemplate, String nodeTypeName, Metadata metaData, Model resourceNode) { + private boolean processModel(Model resourceModel, NodeTemplate resourceNodeTemplate, Metadata metaData, + Model resourceNode) { boolean foundProvidingService = false; if (resourceNode instanceof ProvidingService) { foundProvidingService = true; @@ -497,7 +387,6 @@ public class ArtifactGeneratorToscaParser { if (metaData != null) { resourceNode.populateModelIdentificationInformation(metaData.getAllProperties()); } - idTypeStore.put(resourceNode.getModelNameVersionId(), nodeTypeName); resourceModel.addResource((Resource) resourceNode); } return foundProvidingService; diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index 67c0b2f..bd7144f 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -23,9 +23,10 @@ package org.onap.aai.babel.xml.generator.api; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Map; + import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.onap.aai.babel.logging.ApplicationMsgs; @@ -41,6 +42,7 @@ import org.onap.aai.babel.xml.generator.model.Model; import org.onap.aai.babel.xml.generator.model.ProvidingService; import org.onap.aai.babel.xml.generator.model.Resource; import org.onap.aai.babel.xml.generator.model.Service; +import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget; import org.onap.aai.cl.api.Logger; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; @@ -49,230 +51,300 @@ import org.slf4j.MDC; public class AaiArtifactGenerator implements ArtifactGenerator { - private static Logger log = LogHelper.INSTANCE; - - private static final String MDC_PARAM_MODEL_INFO = "ARTIFACT_MODEL_INFO"; - private static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml"; - private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA = - "Service tosca missing from list of input artifacts"; - private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION = - "Cannot generate artifacts. Service version is not specified"; - private static final String GENERATOR_AAI_INVALID_SERVICE_VERSION = - "Cannot generate artifacts. Service version is incorrect"; - - private AaiModelGenerator modelGenerator = new AaiModelGeneratorImpl(); - - @Override - public GenerationData generateArtifact(byte[] csarArchive, List input, - Map additionalParams) { - Path csarPath; - - try { - csarPath = createTempFile(csarArchive); - } catch (IOException e) { - log.error(ApplicationMsgs.TEMP_FILE_ERROR, e); - return createErrorData(e); - } - - try { - ArtifactGeneratorToscaParser.initWidgetConfiguration(); - ArtifactGeneratorToscaParser.initGroupFilterConfiguration(); - ISdcCsarHelper csarHelper = - SdcToscaParserFactory.getInstance().getSdcCsarHelper(csarPath.toAbsolutePath().toString()); - return generateService(validateServiceVersion(additionalParams), csarHelper); - } catch (Exception e) { - log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); - return createErrorData(e); - } finally { - FileUtils.deleteQuietly(csarPath.toFile()); - } - } - - private GenerationData createErrorData(Exception e) { - GenerationData generationData = new GenerationData(); - generationData.add(ArtifactType.AAI.name(), e.getMessage()); - return generationData; - } - - /** - * Generate model artifacts for the Service and its associated Resources. - * - * @param serviceVersion - * @param csarHelper TOSCA parser - * @return the generated Artifacts - */ - private GenerationData generateService(final String serviceVersion, ISdcCsarHelper csarHelper) { - List serviceNodeTemplates = csarHelper.getServiceNodeTemplates(); - if (serviceNodeTemplates == null) { - throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); - } - - // Populate basic service model metadata - Service serviceModel = new Service(); - serviceModel.setModelVersion(serviceVersion); - serviceModel.populateModelIdentificationInformation(csarHelper.getServiceMetadataAllProperties()); - - Map idTypeStore = new HashMap<>(); - - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); - if (!serviceNodeTemplates.isEmpty()) { - parser.processServiceTosca(serviceModel, idTypeStore, serviceNodeTemplates); - } - - // Process the resource TOSCA files - List resources = parser.processResourceToscas(serviceNodeTemplates, idTypeStore); - - MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); - String aaiServiceModel = modelGenerator.generateModelFor(serviceModel); - - GenerationData generationData = new GenerationData(); - generationData.add(getServiceArtifact(serviceModel, aaiServiceModel)); - - // Generate AAI XML resource model - for (Resource resource : resources) { - generateResourceArtifact(generationData, resource); - for (Resource childResource : resource.getResources()) { - if (!(childResource instanceof ProvidingService)) { - generateResourceArtifact(generationData, childResource); - } - } - } - - return generationData; - } - - /** - * @param generationData - * @param resource - */ - private void generateResourceArtifact(GenerationData generationData, Resource resource) { - if (!isContained(generationData, getArtifactName(resource))) { - log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating resource model"); - Artifact resourceArtifact = getResourceArtifact(resource, modelGenerator.generateModelFor(resource)); - generationData.add(resourceArtifact); - } - } - - private Path createTempFile(byte[] bytes) throws IOException { - log.debug("Creating temp file on file system for the csar"); - Path path = Files.createTempFile("temp", ".csar"); - Files.write(path, bytes); - return path; - } - - /** - * Create the artifact label for an AAI model. - * - * @param model - * @return the artifact label as String - */ - private String getArtifactLabel(Model model) { - StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); - artifactName.append("-"); - artifactName.append(model.getModelType().name().toLowerCase()); - artifactName.append("-"); - artifactName.append(hashCodeUuId(model.getModelNameVersionId())); - return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-"); - } - - /** - * Method to generate the artifact name for an AAI model. - * - * @param model AAI artifact model - * @return Model artifact name - */ - private String getArtifactName(Model model) { - StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); - artifactName.append("-"); - - String truncatedArtifactName = truncateName(model.getModelName()); - artifactName.append(truncatedArtifactName); - - artifactName.append("-"); - artifactName.append(model.getModelType().name().toLowerCase()); - artifactName.append("-"); - artifactName.append(model.getModelVersion()); - - artifactName.append("."); - artifactName.append(GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION); - return artifactName.toString(); - } - - /** - * Create Resource artifact model from the AAI xml model string. - * - * @param resourceModel Model of the resource artifact - * @param aaiResourceModel AAI model as string - * @return Generated {@link Artifact} model for the resource - */ - private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { - final String resourceArtifactLabel = getArtifactLabel(resourceModel); - MDC.put(MDC_PARAM_MODEL_INFO, resourceModel.getModelName() + "," + resourceArtifactLabel); - final byte[] bytes = aaiResourceModel.getBytes(); - - Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), - GeneratorUtil.checkSum(bytes), GeneratorUtil.encode(bytes)); - artifact.setName(getArtifactName(resourceModel)); - artifact.setLabel(resourceArtifactLabel); - artifact.setDescription(ArtifactGeneratorToscaParser.getArtifactDescription(resourceModel)); - return artifact; - } - - /** - * @param generationData - * @param artifactName - * @return - */ - private boolean isContained(GenerationData generationData, final String artifactName) { - return generationData.getResultData().stream() - .anyMatch(artifact -> StringUtils.equals(artifact.getName(), artifactName)); - } - - /** - * Create Service artifact model from the AAI xml model string. - * - * @param serviceModel Model of the service artifact - * @param aaiServiceModel AAI model as string - * @return Generated {@link Artifact} model for the service - */ - private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { - Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), - GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes())); - String serviceArtifactName = getArtifactName(serviceModel); - String serviceArtifactLabel = getArtifactLabel(serviceModel); - artifact.setName(serviceArtifactName); - artifact.setLabel(serviceArtifactLabel); - String description = ArtifactGeneratorToscaParser.getArtifactDescription(serviceModel); - artifact.setDescription(description); - return artifact; - } - - private int hashCodeUuId(String uuId) { - int hashcode = 0; - for (int i = 0; i < uuId.length(); i++) { - hashcode = 31 * hashcode + uuId.charAt(i); - } - return hashcode; - } - - private String truncateName(String name) { - String truncatedName = name; - if (name.length() >= 200) { - truncatedName = name.substring(0, 199); - } - return truncatedName; - } - - private String validateServiceVersion(Map additionalParams) { - String serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName()); - if (serviceVersion == null) { - throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION); - } else { - String versionRegex = "^[1-9]\\d*(\\.0)$"; - if (!(serviceVersion.matches(versionRegex))) { - throw new IllegalArgumentException(String.format(GENERATOR_AAI_INVALID_SERVICE_VERSION)); - } - } - return serviceVersion; - } + private static Logger log = LogHelper.INSTANCE; + + private static final String MDC_PARAM_MODEL_INFO = "ARTIFACT_MODEL_INFO"; + private static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml"; + private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA = "Service tosca missing from list of input artifacts"; + private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION = "Cannot generate artifacts. Service version is not specified"; + private static final String GENERATOR_AAI_INVALID_SERVICE_VERSION = "Cannot generate artifacts. Service version is incorrect"; + + private AaiModelGenerator modelGenerator = new AaiModelGeneratorImpl(); + + @Override + public GenerationData generateArtifact(byte[] csarArchive, List input, + Map additionalParams) { + Path csarPath; + + try { + csarPath = createTempFile(csarArchive); + } catch (IOException e) { + log.error(ApplicationMsgs.TEMP_FILE_ERROR, e); + return createErrorData(e); + } + + try { + ArtifactGeneratorToscaParser.initWidgetConfiguration(); + ArtifactGeneratorToscaParser.initGroupFilterConfiguration(); + ISdcCsarHelper csarHelper = SdcToscaParserFactory.getInstance() + .getSdcCsarHelper(csarPath.toAbsolutePath().toString()); + return generateAllArtifacts(validateServiceVersion(additionalParams), csarHelper); + } catch (Exception e) { + log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); + return createErrorData(e); + } finally { + FileUtils.deleteQuietly(csarPath.toFile()); + } + } + + private GenerationData createErrorData(Exception e) { + GenerationData generationData = new GenerationData(); + generationData.add(ArtifactType.AAI.name(), e.getMessage()); + return generationData; + } + + /** + * Generate model artifacts for the Service and its associated Resources. + * + * @param serviceVersion + * @param csarHelper + * interface to the TOSCA parser + * @return the generated Artifacts (containing XML models) + */ + private GenerationData generateAllArtifacts(final String serviceVersion, ISdcCsarHelper csarHelper) { + List serviceNodeTemplates = csarHelper.getServiceNodeTemplates(); + if (serviceNodeTemplates == null) { + throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); + } + + Service serviceModel = createServiceModel(serviceVersion, csarHelper.getServiceMetadataAllProperties()); + + MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); + + List resources = generateResourceModels(csarHelper, serviceNodeTemplates, serviceModel); + + // Generate the A&AI XML model for the Service. + final String serviceArtifact = modelGenerator.generateModelFor(serviceModel); + + // Build a Babel Artifact to be returned to the caller. + GenerationData generationData = new GenerationData(); + generationData.add(getServiceArtifact(serviceModel, serviceArtifact)); + + // For each Resource, generate the A&AI XML model and then create an additional Artifact for that model. + for (Resource resource : resources) { + generateResourceArtifact(generationData, resource); + for (Resource childResource : resource.getResources()) { + if (!(childResource instanceof ProvidingService)) { + generateResourceArtifact(generationData, childResource); + } + } + } + + return generationData; + } + + /** + * Create a Service from the provided metadata + * + * @param serviceVersion + * @param properties + * @return + */ + private Service createServiceModel(final String serviceVersion, Map properties) { + log.debug("Processing (TOSCA) Service object"); + Service serviceModel = new Service(); + serviceModel.setModelVersion(serviceVersion); + serviceModel.populateModelIdentificationInformation(properties); + return serviceModel; + } + + /** + * @param csarHelper + * @param serviceNodeTemplates + * @param serviceModel + * @return the generated Models + */ + private List generateResourceModels(ISdcCsarHelper csarHelper, List serviceNodeTemplates, + Service serviceModel) { + final ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); + + List resources = new ArrayList<>(); + + for (NodeTemplate nodeTemplate : serviceNodeTemplates) { + if (nodeTemplate.getMetaData() != null) { + generateModelFromNodeTemplate(csarHelper, serviceModel, resources, parser, nodeTemplate); + } else { + log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName()); + } + } + + return resources; + } + + private void generateModelFromNodeTemplate(ISdcCsarHelper csarHelper, Service serviceModel, + List resources, ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) { + String nodeTypeName = parser.normaliseNodeTypeName(nodeTemplate); + Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + if (model != null) { + if (nodeTemplate.getMetaData() != null) { + model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); + } + + parser.addRelatedModel(serviceModel, model); + if (model instanceof Resource) { + generateResourceModel(csarHelper, resources, parser, nodeTemplate, nodeTypeName); + } + } + } + + private void generateResourceModel(ISdcCsarHelper csarHelper, List resources, + ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate, String nodeTypeName) { + log.debug("Processing resource " + nodeTypeName + ": " + nodeTemplate.getMetaData().getValue("UUID")); + Model resourceModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + + Map serviceMetadata = nodeTemplate.getMetaData().getAllProperties(); + resourceModel.populateModelIdentificationInformation(serviceMetadata); + + parser.processResourceModels(resourceModel, csarHelper.getNodeTemplateChildren(nodeTemplate)); + + if (csarHelper.getServiceVfList() != null) { + parser.processVfModules(resources, resourceModel, nodeTemplate); + } + + if (parser.hasSubCategoryTunnelXConnect(serviceMetadata) && parser.hasAllottedResource(serviceMetadata)) { + resourceModel.addWidget(new TunnelXconnectWidget()); + } + + resources.addAll(parser.processInstanceGroups(resourceModel, nodeTemplate)); + resources.add((Resource) resourceModel); + } + + /** + * @param generationData + * @param resource + */ + private void generateResourceArtifact(GenerationData generationData, Resource resource) { + if (!isContained(generationData, getArtifactName(resource))) { + log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating resource model"); + generationData.add(getResourceArtifact(resource, modelGenerator.generateModelFor(resource))); + } + } + + private Path createTempFile(byte[] bytes) throws IOException { + log.debug("Creating temp file on file system for the csar"); + Path path = Files.createTempFile("temp", ".csar"); + Files.write(path, bytes); + return path; + } + + /** + * Create the artifact label for an AAI model. + * + * @param model + * @return the artifact label as String + */ + private String getArtifactLabel(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(hashCodeUuId(model.getModelNameVersionId())); + return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-"); + } + + /** + * Method to generate the artifact name for an AAI model. + * + * @param model + * AAI artifact model + * @return Model artifact name + */ + private String getArtifactName(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + + String truncatedArtifactName = truncateName(model.getModelName()); + artifactName.append(truncatedArtifactName); + + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(model.getModelVersion()); + + artifactName.append("."); + artifactName.append(GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION); + return artifactName.toString(); + } + + /** + * Create Resource artifact model from the AAI xml model string. + * + * @param resourceModel + * Model of the resource artifact + * @param aaiResourceModel + * AAI model as string + * @return Generated {@link Artifact} model for the resource + */ + private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { + final String resourceArtifactLabel = getArtifactLabel(resourceModel); + MDC.put(MDC_PARAM_MODEL_INFO, resourceModel.getModelName() + "," + resourceArtifactLabel); + final byte[] bytes = aaiResourceModel.getBytes(); + + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(bytes), GeneratorUtil.encode(bytes)); + artifact.setName(getArtifactName(resourceModel)); + artifact.setLabel(resourceArtifactLabel); + artifact.setDescription(ArtifactGeneratorToscaParser.getArtifactDescription(resourceModel)); + return artifact; + } + + /** + * @param generationData + * @param artifactName + * @return + */ + private boolean isContained(GenerationData generationData, final String artifactName) { + return generationData.getResultData().stream() + .anyMatch(artifact -> StringUtils.equals(artifact.getName(), artifactName)); + } + + /** + * Create Service artifact model from the AAI XML model. + * + * @param serviceModel + * Model of the service artifact + * @param aaiServiceModel + * AAI model as string + * @return Generated {@link Artifact} model for the service + */ + private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes())); + String serviceArtifactName = getArtifactName(serviceModel); + String serviceArtifactLabel = getArtifactLabel(serviceModel); + artifact.setName(serviceArtifactName); + artifact.setLabel(serviceArtifactLabel); + String description = ArtifactGeneratorToscaParser.getArtifactDescription(serviceModel); + artifact.setDescription(description); + return artifact; + } + + private int hashCodeUuId(String uuId) { + int hashcode = 0; + for (int i = 0; i < uuId.length(); i++) { + hashcode = 31 * hashcode + uuId.charAt(i); + } + return hashcode; + } + + private String truncateName(String name) { + String truncatedName = name; + if (name.length() >= 200) { + truncatedName = name.substring(0, 199); + } + return truncatedName; + } + + private String validateServiceVersion(Map additionalParams) { + String serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName()); + if (serviceVersion == null) { + throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION); + } else { + String versionRegex = "^[1-9]\\d*(\\.0)$"; + if (!(serviceVersion.matches(versionRegex))) { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_INVALID_SERVICE_VERSION)); + } + } + return serviceVersion; + } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java index 9d4feab..b3d42ec 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java @@ -22,33 +22,34 @@ package org.onap.aai.babel.xml.generator.model; public class Resource extends Model { - @Override - public int hashCode() { - return getModelNameVersionId().hashCode(); - } + @Override + public int hashCode() { + final String uuid = getModelNameVersionId(); + return uuid == null ? 0 : uuid.hashCode(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof Resource) { - return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); - } - return false; - } + @Override + public boolean equals(Object obj) { + if (obj instanceof Resource) { + return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); + } + return false; + } - @Override - public boolean addResource(Resource resource) { - return resources.add(resource); - } + @Override + public boolean addResource(Resource resource) { + return resources.add(resource); + } - @Override - public boolean addWidget(Widget widget) { - return widgets.add(widget); - } + @Override + public boolean addWidget(Widget widget) { + return widgets.add(widget); + } - @Override - public Widget.Type getWidgetType() { - org.onap.aai.babel.xml.generator.types.Model model = - this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); - return model.widget(); - } + @Override + public Widget.Type getWidgetType() { + org.onap.aai.babel.xml.generator.types.Model model = this.getClass() + .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return model.widget(); + } } diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index 6cf6d31..3cbaa13 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -30,9 +30,11 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Properties; + import org.junit.Test; import org.mockito.Mockito; 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.Resource; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; @@ -41,7 +43,8 @@ import org.onap.sdc.toscaparser.api.NodeTemplate; import org.onap.sdc.toscaparser.api.SubstitutionMappings; /** - * Direct tests of the TOSCA parser-based Artifact Generator, to cover exceptional cases. + * Direct tests of the TOSCA parser-based Artifact Generator {@link ArtifactGeneratorToscaParser}., to cover exceptional + * cases. */ public class TestArtifactGeneratorToscaParser { @@ -49,14 +52,23 @@ public class TestArtifactGeneratorToscaParser { private static final String TEST_UUID = "1234"; /** - * Process a dummy Node Template object for a Service. A WARNING should be logged for the missing metadata. + * Process an Allotted Resource that does not have a Providing Service. */ - @Test - public void testMissingServiceData() { + @Test(expected = IllegalArgumentException.class) + public void testMissingProvidingService() { List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(null); - parser.processServiceTosca(null, Collections.emptyMap(), nodeTemplateList); - parser.processResourceToscas(nodeTemplateList, null); + new ArtifactGeneratorToscaParser(null).processResourceModels(new AllotedResource(), nodeTemplateList); + } + + /** + * + * Add a CR (a type of Resource which is not a Providing Service) to a Resource Model. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddResourceNotProvidingService() { + List nodeTemplateList = Collections.singletonList(buildNodeTemplate("testCR", "CR")); + final Resource dummyResource = new AllotedResource(); // Any Resource to which the CR can be added + new ArtifactGeneratorToscaParser(null).processResourceModels(dummyResource, nodeTemplateList); } /** @@ -72,22 +84,33 @@ public class TestArtifactGeneratorToscaParser { ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); - NodeTemplate serviceNode = buildNodeTemplate("service", "org.openecomp.resource.cr.a-collection-resource"); - serviceNode.setSubMappingToscaTemplate(sm); - Mockito.when(helper.getNodeTemplateByName(serviceNode.getName())).thenReturn(serviceNode); + NodeTemplate serviceNodeTemplate = buildNodeTemplate("service", + "org.openecomp.resource.cr.a-collection-resource"); + serviceNodeTemplate.setSubMappingToscaTemplate(sm); + Mockito.when(helper.getNodeTemplateByName(serviceNodeTemplate.getName())).thenReturn(serviceNodeTemplate); ArrayList groups = new ArrayList<>(); groups.add(buildGroup("group", instanceGroupType)); - Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNode)).thenReturn(groups); + Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate)).thenReturn(groups); ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); - List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNode); + List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNodeTemplate); assertThat(resources.size(), is(1)); Resource resource = resources.get(0); assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); } + /** + * Create a NodeTemplate for unit testing purposes. In production code this object would only be created by the + * sdc-tosca parser. + * + * @param name + * name of the NodeTemplate + * @param type + * type of the NodeTemplate + * @return a new NodeTemplate object + */ private NodeTemplate buildNodeTemplate(String name, String type) { LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); nodeTemplateMap.put(name, buildMap("type", type)); -- 2.16.6 From 047862bba53addd381fc7c715ac9e3dff76b740d Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Tue, 27 Nov 2018 12:38:09 +0000 Subject: [PATCH 05/16] Refactor processProvidingService method Remedy some Java formatting issues and also simplify Providing Service processing code. Change-Id: Ifddc045e6a1fd1280f63065d4f3b6df83251800c Issue-ID: AAI-1884 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 726 +++++++++++---------- .../aai/babel/xml/generator/model/Resource.java | 52 +- 2 files changed, 396 insertions(+), 382 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index 827e552..cae0324 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright � 2017-2018 AT&T Intellectual Property. All rights reserved. + * Copyright � 2017-2018 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ * limitations under the License. * ============LICENSE_END========================================================= */ - package org.onap.aai.babel.parser; import java.io.File; @@ -32,7 +31,6 @@ import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; import java.util.stream.Stream; - 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; @@ -43,7 +41,6 @@ import org.onap.aai.babel.xml.generator.model.ProvidingService; import org.onap.aai.babel.xml.generator.model.Resource; import org.onap.aai.babel.xml.generator.model.VfModule; import org.onap.aai.babel.xml.generator.model.Widget; -import org.onap.aai.babel.xml.generator.types.ModelType; import org.onap.aai.cl.api.Logger; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.toscaparser.api.Group; @@ -51,357 +48,374 @@ import org.onap.sdc.toscaparser.api.NodeTemplate; import org.onap.sdc.toscaparser.api.Property; import org.onap.sdc.toscaparser.api.elements.Metadata; +/** + * Wrapper for the sdc-tosca parser + * + */ 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. 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 = "Cannot generate artifacts. Providing Service is missing for allotted resource %s"; - - // Metadata properties - private static final String CATEGORY = "category"; - private static final String ALLOTTED_RESOURCE = "Allotted Resource"; - private static final String SUBCATEGORY = "subcategory"; - private static final String TUNNEL_XCONNECT = "Tunnel XConnect"; - - private static final String VERSION = "version"; - - private ISdcCsarHelper csarHelper; - - /** - * Constructs using csarHelper - * - * @param csarHelper - * The csar helper - */ - public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { - this.csarHelper = csarHelper; - } - - /** - * Returns the artifact description - * - * @param model - * the artifact model - * @return the artifact model's description - */ - public static String getArtifactDescription(Model model) { - String artifactDesc = model.getModelDescription(); - if (model.getModelType().equals(ModelType.SERVICE)) { - artifactDesc = "AAI Service Model"; - } else if (model.getModelType().equals(ModelType.RESOURCE)) { - artifactDesc = "AAI Resource Model"; - } - return artifactDesc; - } - - /** - * Initialises the widget configuration. - * - * @throws IOException - */ - public static void initWidgetConfiguration() throws IOException { - log.debug("Getting Widget Configuration"); - String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); - if (configLocation != null) { - File file = new File(configLocation); - if (file.exists()) { - Properties properties = new Properties(); - properties.load(new FileInputStream(file)); - WidgetConfigurationUtil.setConfig(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_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 groups for this service node, according to the defined filter. - * - * @param resourceModel - * @param serviceNodeTemplate - * @return resources for which XML Models should be generated - */ - public List processInstanceGroups(Model resourceModel, NodeTemplate serviceNodeTemplate) { - List resources = new ArrayList<>(); - if (serviceNodeTemplate.getSubMappingToscaTemplate() != null) { - List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate); - for (Group group : serviceGroups) { - if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { - resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), - group.getMetadata().getAllProperties(), group.getProperties())); - } - } - } - return resources; - } - - /** - * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are - * duplicate keys then the TOSCA Property value takes precedence. - * - * @param stringProps - * initial Map of String property values (e.g. from the TOSCA YAML metadata section) - * @param toscaProps - * Map of TOSCA Property Type Object values to merge in (or overwrite) - * @return a Map of the property values converted to String - */ - public Map mergeProperties(Map stringProps, Map toscaProps) { - Map props = new HashMap<>(stringProps); - toscaProps.forEach((key, toscaProp) -> props.put(key, - toscaProp.getValue() == null ? "" : toscaProp.getValue().toString())); - return props; - } - - public Resource createInstanceGroupModel(Map properties) { - Resource groupModel = new InstanceGroup(); - groupModel.populateModelIdentificationInformation(properties); - return groupModel; - } - - /** - * @param model - * @param relation - */ - public void addRelatedModel(final Model model, final Model relation) { - if (relation instanceof Resource) { - model.addResource((Resource) relation); - } else { - model.addWidget((Widget) relation); - } - } - - public 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.")) { - nodeTypeName = "org.openecomp.resource.vf.allottedResource"; - } - if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { - nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; - } - } - return nodeTypeName; - } - - public boolean hasAllottedResource(Map metadata) { - return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); - } - - public boolean hasSubCategoryTunnelXConnect(Map metadata) { - return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); - } - - /** - * Process TOSCA Group information for VF Modules. - * - * @param resources - * @param model - * @param serviceNode - */ - public void processVfModules(List 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); - List serviceGroups = csarHelper.getVfModulesByVf(uuid); - - // Process each VF Group - for (Group serviceGroup : serviceGroups) { - Model groupModel = Model.getModelFor(serviceGroup.getType()); - if (groupModel instanceof VfModule) { - processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); - } - } - } - - /** - * @param resourceModel - * @param resourceNodeTemplates - */ - public void processResourceModels(Model resourceModel, List resourceNodeTemplates) { - boolean foundProvidingService = false; - - for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { - 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(resourceModel, resourceNodeTemplate, metaData, resourceNode); - } - - if (resourceModel instanceof AllotedResource && !foundProvidingService) { - final String modelInvariantId = resourceModel.getModelId(); - throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, - modelInvariantId == null ? "" : modelInvariantId)); - } - } - - /** - * 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 processInstanceGroup(Model resourceModel, ArrayList memberNodes, - Map metaProperties, Map properties) { - Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties)); - resourceModel.addResource(groupModel); - List resources = Stream.of(groupModel).collect(Collectors.toList()); - - if (memberNodes != null && !memberNodes.isEmpty()) { - resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel)); - } - - return resources; - } - - /** - * @param memberNodes - * @param groupModel - * @return - */ - private List generateResourcesAndWidgets(final ArrayList memberNodes, - final Resource groupModel) { - List 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; - } - - private void processVfModule(List resources, Model vfModel, Group groupDefinition, - NodeTemplate serviceNode, VfModule groupModel) { - groupModel.populateModelIdentificationInformation( - mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); - - processVfModuleGroup(groupModel, csarHelper.getMembersOfVfModule(serviceNode, groupDefinition)); - - vfModel.addResource(groupModel); // Add group (VfModule) to the (VF) model - // Check if we have already encountered the same VfModule across all the artifacts - if (!resources.contains(groupModel)) { - resources.add(groupModel); - } - } - - private void processVfModuleGroup(VfModule groupModel, List members) { - if (members != null && !members.isEmpty()) { - // Get names of the members of the service group - List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); - groupModel.setMembers(memberNames); - for (NodeTemplate member : members) { - processGroupMembers(groupModel, member); - } - } - } - - private void processGroupMembers(Model group, NodeTemplate member) { - Model resourceNode; - // L3-network inside vf-module to be generated as Widget a special handling. - if (member.getType().contains("org.openecomp.resource.vl")) { - resourceNode = new L3NetworkWidget(); - } else { - resourceNode = Model.getModelFor(member.getType()); - } - if (resourceNode != null && !(resourceNode instanceof Resource)) { - Widget widget = (Widget) resourceNode; - widget.addKey(member.getName()); - // Add the widget element encountered to the Group model - group.addWidget(widget); - } - } - - /** - * Create a Map of property name against String property value from the input Map - * - * @param inputMap - * The input Map - * @return Map of property name against String property value - */ - private Map populateStringProperties(Map inputMap) { - return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString())); - } - - /** - * @param resourceModel - * @param resourceNodeTemplate - * @param metaData - * @param resourceNode - * @return - */ - private boolean processModel(Model resourceModel, NodeTemplate resourceNodeTemplate, 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()); - } - resourceModel.addResource((Resource) resourceNode); - } - return foundProvidingService; - } - - private void processProvidingService(Model resourceModel, NodeTemplate resourceNodeTemplate, Model resourceNode) { - Map 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, resourceModel.getModelId())); - } - Map properties = populateStringProperties(nodeProperties); - properties.put(VERSION, "1.0"); - resourceNode.populateModelIdentificationInformation(properties); - resourceModel.addResource((Resource) resourceNode); - } + 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. 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 = + "Cannot generate artifacts. Providing Service is missing for allotted resource %s"; + + // Metadata properties + private static final String CATEGORY = "category"; + private static final String ALLOTTED_RESOURCE = "Allotted Resource"; + private static final String SUBCATEGORY = "subcategory"; + private static final String TUNNEL_XCONNECT = "Tunnel XConnect"; + + private static final String VERSION = "version"; + + private ISdcCsarHelper csarHelper; + + /** + * Constructs using csarHelper + * + * @param csarHelper + * The csar helper + */ + public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { + this.csarHelper = csarHelper; + } + + /** + * Get or create the artifact description. + * + * @param model + * the artifact model + * @return the artifact model's description + */ + public static String getArtifactDescription(Model model) { + switch (model.getModelType()) { + case SERVICE: + return "AAI Service Model"; + case RESOURCE: + return "AAI Resource Model"; + default: + return model.getModelDescription(); + } + } + + /** + * Initialises the widget configuration. + * + * @throws IOException + */ + public static void initWidgetConfiguration() throws IOException { + log.debug("Getting Widget Configuration"); + String configLocation = System.getProperty(PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + if (configLocation != null) { + File file = new File(configLocation); + if (file.exists()) { + Properties properties = new Properties(); + properties.load(new FileInputStream(file)); + WidgetConfigurationUtil.setConfig(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_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 groups for this service node, according to the defined filter. + * + * @param resourceModel + * @param serviceNodeTemplate + * @return resources for which XML Models should be generated + */ + public List processInstanceGroups(Model resourceModel, NodeTemplate serviceNodeTemplate) { + List resources = new ArrayList<>(); + if (serviceNodeTemplate.getSubMappingToscaTemplate() != null) { + List serviceGroups = csarHelper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate); + for (Group group : serviceGroups) { + if (WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { + resources.addAll(processInstanceGroup(resourceModel, group.getMemberNodes(), + group.getMetadata().getAllProperties(), group.getProperties())); + } + } + } + return resources; + } + + /** + * Merge a Map of String values with a Map of TOSCA Property Objects to create a combined Map. If there are + * duplicate keys then the TOSCA Property value takes precedence. + * + * @param stringProps + * initial Map of String property values (e.g. from the TOSCA YAML metadata section) + * @param toscaProps + * Map of TOSCA Property Type Object values to merge in (or overwrite) + * @return a Map of the property values converted to String + */ + public Map mergeProperties(Map stringProps, Map toscaProps) { + Map props = new HashMap<>(stringProps); + toscaProps.forEach((key, toscaProp) -> props.put(key, + toscaProp.getValue() == null ? "" : toscaProp.getValue().toString())); + return props; + } + + public Resource createInstanceGroupModel(Map properties) { + Resource groupModel = new InstanceGroup(); + groupModel.populateModelIdentificationInformation(properties); + return groupModel; + } + + /** + * @param model + * @param relation + */ + public void addRelatedModel(final Model model, final Model relation) { + if (relation instanceof Resource) { + model.addResource((Resource) relation); + } else { + model.addWidget((Widget) relation); + } + } + + public 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.")) { + nodeTypeName = "org.openecomp.resource.vf.allottedResource"; + } + if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { + nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; + } + } + return nodeTypeName; + } + + public boolean hasAllottedResource(Map metadata) { + return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); + } + + public boolean hasSubCategoryTunnelXConnect(Map metadata) { + return TUNNEL_XCONNECT.equals(metadata.get(SUBCATEGORY)); + } + + /** + * Process TOSCA Group information for VF Modules. + * + * @param resources + * @param model + * @param serviceNode + */ + public void processVfModules(List 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); + List serviceGroups = csarHelper.getVfModulesByVf(uuid); + + // Process each VF Group + for (Group serviceGroup : serviceGroups) { + Model groupModel = Model.getModelFor(serviceGroup.getType()); + if (groupModel instanceof VfModule) { + processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); + } + } + } + + /** + * @param resourceModel + * @param resourceNodeTemplates + */ + public void processResourceModels(Model resourceModel, List resourceNodeTemplates) { + boolean foundProvidingService = false; + + for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { + 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(resourceModel, metaData, resourceNode, resourceNodeTemplate.getProperties()); + } + + if (resourceModel instanceof AllotedResource && !foundProvidingService) { + final String modelInvariantId = resourceModel.getModelId(); + throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, + modelInvariantId == null ? "" : modelInvariantId)); + } + } + + /** + * 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 processInstanceGroup(Model resourceModel, ArrayList memberNodes, + Map metaProperties, Map properties) { + Resource groupModel = createInstanceGroupModel(mergeProperties(metaProperties, properties)); + resourceModel.addResource(groupModel); + List resources = Stream.of(groupModel).collect(Collectors.toList()); + + if (memberNodes != null && !memberNodes.isEmpty()) { + resources.addAll(generateResourcesAndWidgets(memberNodes, groupModel)); + } + + return resources; + } + + /** + * @param memberNodes + * @param groupModel + * @return + */ + private List generateResourcesAndWidgets(final ArrayList memberNodes, + final Resource groupModel) { + List 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; + } + + private void processVfModule(List resources, Model vfModel, Group groupDefinition, + NodeTemplate serviceNode, VfModule groupModel) { + groupModel.populateModelIdentificationInformation( + mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); + + processVfModuleGroup(groupModel, csarHelper.getMembersOfVfModule(serviceNode, groupDefinition)); + + vfModel.addResource(groupModel); // Add group (VfModule) to the (VF) model + // Check if we have already encountered the same VfModule across all the artifacts + if (!resources.contains(groupModel)) { + resources.add(groupModel); + } + } + + private void processVfModuleGroup(VfModule groupModel, List members) { + if (members != null && !members.isEmpty()) { + // Get names of the members of the service group + List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); + groupModel.setMembers(memberNames); + for (NodeTemplate member : members) { + processGroupMembers(groupModel, member); + } + } + } + + private void processGroupMembers(Model group, NodeTemplate member) { + Model resourceNode; + // L3-network inside vf-module to be generated as Widget a special handling. + if (member.getType().contains("org.openecomp.resource.vl")) { + resourceNode = new L3NetworkWidget(); + } else { + resourceNode = Model.getModelFor(member.getType()); + } + if (resourceNode != null && !(resourceNode instanceof Resource)) { + Widget widget = (Widget) resourceNode; + widget.addKey(member.getName()); + // Add the widget element encountered to the Group model + group.addWidget(widget); + } + } + + /** + * Create a Map of property name against String property value from the input Map + * + * @param inputMap + * The input Map + * @return Map of property name against String property value + */ + private Map populateStringProperties(Map inputMap) { + return inputMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().getValue() == null ? "" : e.getValue().getValue().toString())); + } + + /** + * If the specified resourceNode is a type of Resource, add it to the specified resourceModel. If the Resource type + * is ProvidingService return true, otherwise return false. + * + * @param resourceModel + * parent Resource + * @param metaData + * metadata for populating the Resource IDs + * @param resourceNode + * any Model (will be ignored if not a Resource) + * @param nodeProperties + * the node properties + * @return whether or not a ProvidingService was prcoessed + */ + private boolean processModel(Model resourceModel, Metadata metaData, Model resourceNode, + Map nodeProperties) { + boolean foundProvidingService = false; + if (resourceNode instanceof ProvidingService) { + foundProvidingService = true; + processProvidingService(resourceModel, resourceNode, nodeProperties); + } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) { + if (metaData != null) { + resourceNode.populateModelIdentificationInformation(metaData.getAllProperties()); + } + resourceModel.addResource((Resource) resourceNode); + } + return foundProvidingService; + } + + private void processProvidingService(Model resourceModel, Model resourceNode, + Map nodeProperties) { + if (nodeProperties == null || nodeProperties.get("providing_service_uuid") == null + || nodeProperties.get("providing_service_invariant_uuid") == null) { + throw new IllegalArgumentException( + String.format(GENERATOR_AAI_PROVIDING_SERVICE_METADATA_MISSING, resourceModel.getModelId())); + } + Map properties = populateStringProperties(nodeProperties); + properties.put(VERSION, "1.0"); + resourceNode.populateModelIdentificationInformation(properties); + resourceModel.addResource((Resource) resourceNode); + } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java index b3d42ec..1b64907 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java @@ -22,34 +22,34 @@ package org.onap.aai.babel.xml.generator.model; public class Resource extends Model { - @Override - public int hashCode() { - final String uuid = getModelNameVersionId(); - return uuid == null ? 0 : uuid.hashCode(); - } + @Override + public int hashCode() { + final String uuid = getModelNameVersionId(); + return uuid == null ? 0 : uuid.hashCode(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof Resource) { - return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); - } - return false; - } + @Override + public boolean equals(Object obj) { + if (obj instanceof Resource) { + return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); + } + return false; + } - @Override - public boolean addResource(Resource resource) { - return resources.add(resource); - } + @Override + public boolean addResource(Resource resource) { + return resources.add(resource); + } - @Override - public boolean addWidget(Widget widget) { - return widgets.add(widget); - } + @Override + public boolean addWidget(Widget widget) { + return widgets.add(widget); + } - @Override - public Widget.Type getWidgetType() { - org.onap.aai.babel.xml.generator.types.Model model = this.getClass() - .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); - return model.widget(); - } + @Override + public Widget.Type getWidgetType() { + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + return model.widget(); + } } -- 2.16.6 From 05e7b934ad49c54c98ce840841528a13e882f8d3 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Tue, 27 Nov 2018 16:54:33 +0000 Subject: [PATCH 06/16] Process Service-level Instance Groups Create an InstanceGroup XML model for groups of the Service topology template in the case where there is no Resource model generated. Add a test CSAR file for a Service Proxy (an unsupported type). Assert that the Service is associated with the instance-group Models. Change-Id: If76bf7a1cfb960bc8692f1e136ee85176725915e Issue-ID: AAI-1963 Signed-off-by: mark.j.leonard --- .../xml/generator/api/AaiArtifactGenerator.java | 613 +++++++++++---------- .../parser/TestArtifactGeneratorToscaParser.java | 221 ++++---- .../aai/babel/service/CsarToXmlConverterTest.java | 10 + .../java/org/onap/aai/babel/testdata/CsarTest.java | 4 +- .../compressedArtifacts/service-S1-csar.csar | Bin 0 -> 67874 bytes .../AAI-Grouping Service for Test-service-1.0.xml | 69 +++ ...ortest..ResourceInstanceGroup..0-resource-1.xml | 32 ++ ...ortest..ResourceInstanceGroup..1-resource-1.xml | 32 ++ 8 files changed, 571 insertions(+), 410 deletions(-) create mode 100644 src/test/resources/compressedArtifacts/service-S1-csar.csar create mode 100644 src/test/resources/generatedXml/AAI-Grouping Service for Test-service-1.0.xml create mode 100644 src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml create mode 100644 src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index bd7144f..531a044 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -26,7 +26,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; - import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.onap.aai.babel.logging.ApplicationMsgs; @@ -38,6 +37,7 @@ import org.onap.aai.babel.xml.generator.data.ArtifactType; import org.onap.aai.babel.xml.generator.data.GenerationData; import org.onap.aai.babel.xml.generator.data.GeneratorUtil; import org.onap.aai.babel.xml.generator.data.GroupType; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; import org.onap.aai.babel.xml.generator.model.Model; import org.onap.aai.babel.xml.generator.model.ProvidingService; import org.onap.aai.babel.xml.generator.model.Resource; @@ -46,305 +46,324 @@ import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget; import org.onap.aai.cl.api.Logger; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; +import org.onap.sdc.toscaparser.api.Group; import org.onap.sdc.toscaparser.api.NodeTemplate; import org.slf4j.MDC; public class AaiArtifactGenerator implements ArtifactGenerator { - private static Logger log = LogHelper.INSTANCE; - - private static final String MDC_PARAM_MODEL_INFO = "ARTIFACT_MODEL_INFO"; - private static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml"; - private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA = "Service tosca missing from list of input artifacts"; - private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION = "Cannot generate artifacts. Service version is not specified"; - private static final String GENERATOR_AAI_INVALID_SERVICE_VERSION = "Cannot generate artifacts. Service version is incorrect"; - - private AaiModelGenerator modelGenerator = new AaiModelGeneratorImpl(); - - @Override - public GenerationData generateArtifact(byte[] csarArchive, List input, - Map additionalParams) { - Path csarPath; - - try { - csarPath = createTempFile(csarArchive); - } catch (IOException e) { - log.error(ApplicationMsgs.TEMP_FILE_ERROR, e); - return createErrorData(e); - } - - try { - ArtifactGeneratorToscaParser.initWidgetConfiguration(); - ArtifactGeneratorToscaParser.initGroupFilterConfiguration(); - ISdcCsarHelper csarHelper = SdcToscaParserFactory.getInstance() - .getSdcCsarHelper(csarPath.toAbsolutePath().toString()); - return generateAllArtifacts(validateServiceVersion(additionalParams), csarHelper); - } catch (Exception e) { - log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); - return createErrorData(e); - } finally { - FileUtils.deleteQuietly(csarPath.toFile()); - } - } - - private GenerationData createErrorData(Exception e) { - GenerationData generationData = new GenerationData(); - generationData.add(ArtifactType.AAI.name(), e.getMessage()); - return generationData; - } - - /** - * Generate model artifacts for the Service and its associated Resources. - * - * @param serviceVersion - * @param csarHelper - * interface to the TOSCA parser - * @return the generated Artifacts (containing XML models) - */ - private GenerationData generateAllArtifacts(final String serviceVersion, ISdcCsarHelper csarHelper) { - List serviceNodeTemplates = csarHelper.getServiceNodeTemplates(); - if (serviceNodeTemplates == null) { - throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); - } - - Service serviceModel = createServiceModel(serviceVersion, csarHelper.getServiceMetadataAllProperties()); - - MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); - - List resources = generateResourceModels(csarHelper, serviceNodeTemplates, serviceModel); - - // Generate the A&AI XML model for the Service. - final String serviceArtifact = modelGenerator.generateModelFor(serviceModel); - - // Build a Babel Artifact to be returned to the caller. - GenerationData generationData = new GenerationData(); - generationData.add(getServiceArtifact(serviceModel, serviceArtifact)); - - // For each Resource, generate the A&AI XML model and then create an additional Artifact for that model. - for (Resource resource : resources) { - generateResourceArtifact(generationData, resource); - for (Resource childResource : resource.getResources()) { - if (!(childResource instanceof ProvidingService)) { - generateResourceArtifact(generationData, childResource); - } - } - } - - return generationData; - } - - /** - * Create a Service from the provided metadata - * - * @param serviceVersion - * @param properties - * @return - */ - private Service createServiceModel(final String serviceVersion, Map properties) { - log.debug("Processing (TOSCA) Service object"); - Service serviceModel = new Service(); - serviceModel.setModelVersion(serviceVersion); - serviceModel.populateModelIdentificationInformation(properties); - return serviceModel; - } - - /** - * @param csarHelper - * @param serviceNodeTemplates - * @param serviceModel - * @return the generated Models - */ - private List generateResourceModels(ISdcCsarHelper csarHelper, List serviceNodeTemplates, - Service serviceModel) { - final ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); - - List resources = new ArrayList<>(); - - for (NodeTemplate nodeTemplate : serviceNodeTemplates) { - if (nodeTemplate.getMetaData() != null) { - generateModelFromNodeTemplate(csarHelper, serviceModel, resources, parser, nodeTemplate); - } else { - log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName()); - } - } - - return resources; - } - - private void generateModelFromNodeTemplate(ISdcCsarHelper csarHelper, Service serviceModel, - List resources, ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) { - String nodeTypeName = parser.normaliseNodeTypeName(nodeTemplate); - Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); - if (model != null) { - if (nodeTemplate.getMetaData() != null) { - model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); - } - - parser.addRelatedModel(serviceModel, model); - if (model instanceof Resource) { - generateResourceModel(csarHelper, resources, parser, nodeTemplate, nodeTypeName); - } - } - } - - private void generateResourceModel(ISdcCsarHelper csarHelper, List resources, - ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate, String nodeTypeName) { - log.debug("Processing resource " + nodeTypeName + ": " + nodeTemplate.getMetaData().getValue("UUID")); - Model resourceModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); - - Map serviceMetadata = nodeTemplate.getMetaData().getAllProperties(); - resourceModel.populateModelIdentificationInformation(serviceMetadata); - - parser.processResourceModels(resourceModel, csarHelper.getNodeTemplateChildren(nodeTemplate)); - - if (csarHelper.getServiceVfList() != null) { - parser.processVfModules(resources, resourceModel, nodeTemplate); - } - - if (parser.hasSubCategoryTunnelXConnect(serviceMetadata) && parser.hasAllottedResource(serviceMetadata)) { - resourceModel.addWidget(new TunnelXconnectWidget()); - } - - resources.addAll(parser.processInstanceGroups(resourceModel, nodeTemplate)); - resources.add((Resource) resourceModel); - } - - /** - * @param generationData - * @param resource - */ - private void generateResourceArtifact(GenerationData generationData, Resource resource) { - if (!isContained(generationData, getArtifactName(resource))) { - log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating resource model"); - generationData.add(getResourceArtifact(resource, modelGenerator.generateModelFor(resource))); - } - } - - private Path createTempFile(byte[] bytes) throws IOException { - log.debug("Creating temp file on file system for the csar"); - Path path = Files.createTempFile("temp", ".csar"); - Files.write(path, bytes); - return path; - } - - /** - * Create the artifact label for an AAI model. - * - * @param model - * @return the artifact label as String - */ - private String getArtifactLabel(Model model) { - StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); - artifactName.append("-"); - artifactName.append(model.getModelType().name().toLowerCase()); - artifactName.append("-"); - artifactName.append(hashCodeUuId(model.getModelNameVersionId())); - return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-"); - } - - /** - * Method to generate the artifact name for an AAI model. - * - * @param model - * AAI artifact model - * @return Model artifact name - */ - private String getArtifactName(Model model) { - StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); - artifactName.append("-"); - - String truncatedArtifactName = truncateName(model.getModelName()); - artifactName.append(truncatedArtifactName); - - artifactName.append("-"); - artifactName.append(model.getModelType().name().toLowerCase()); - artifactName.append("-"); - artifactName.append(model.getModelVersion()); - - artifactName.append("."); - artifactName.append(GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION); - return artifactName.toString(); - } - - /** - * Create Resource artifact model from the AAI xml model string. - * - * @param resourceModel - * Model of the resource artifact - * @param aaiResourceModel - * AAI model as string - * @return Generated {@link Artifact} model for the resource - */ - private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { - final String resourceArtifactLabel = getArtifactLabel(resourceModel); - MDC.put(MDC_PARAM_MODEL_INFO, resourceModel.getModelName() + "," + resourceArtifactLabel); - final byte[] bytes = aaiResourceModel.getBytes(); - - Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), - GeneratorUtil.checkSum(bytes), GeneratorUtil.encode(bytes)); - artifact.setName(getArtifactName(resourceModel)); - artifact.setLabel(resourceArtifactLabel); - artifact.setDescription(ArtifactGeneratorToscaParser.getArtifactDescription(resourceModel)); - return artifact; - } - - /** - * @param generationData - * @param artifactName - * @return - */ - private boolean isContained(GenerationData generationData, final String artifactName) { - return generationData.getResultData().stream() - .anyMatch(artifact -> StringUtils.equals(artifact.getName(), artifactName)); - } - - /** - * Create Service artifact model from the AAI XML model. - * - * @param serviceModel - * Model of the service artifact - * @param aaiServiceModel - * AAI model as string - * @return Generated {@link Artifact} model for the service - */ - private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { - Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), - GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes())); - String serviceArtifactName = getArtifactName(serviceModel); - String serviceArtifactLabel = getArtifactLabel(serviceModel); - artifact.setName(serviceArtifactName); - artifact.setLabel(serviceArtifactLabel); - String description = ArtifactGeneratorToscaParser.getArtifactDescription(serviceModel); - artifact.setDescription(description); - return artifact; - } - - private int hashCodeUuId(String uuId) { - int hashcode = 0; - for (int i = 0; i < uuId.length(); i++) { - hashcode = 31 * hashcode + uuId.charAt(i); - } - return hashcode; - } - - private String truncateName(String name) { - String truncatedName = name; - if (name.length() >= 200) { - truncatedName = name.substring(0, 199); - } - return truncatedName; - } - - private String validateServiceVersion(Map additionalParams) { - String serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName()); - if (serviceVersion == null) { - throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION); - } else { - String versionRegex = "^[1-9]\\d*(\\.0)$"; - if (!(serviceVersion.matches(versionRegex))) { - throw new IllegalArgumentException(String.format(GENERATOR_AAI_INVALID_SERVICE_VERSION)); - } - } - return serviceVersion; - } + private static Logger log = LogHelper.INSTANCE; + + private static final String MDC_PARAM_MODEL_INFO = "ARTIFACT_MODEL_INFO"; + private static final String GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION = "xml"; + private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA = + "Service tosca missing from list of input artifacts"; + private static final String GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION = + "Cannot generate artifacts. Service version is not specified"; + private static final String GENERATOR_AAI_INVALID_SERVICE_VERSION = + "Cannot generate artifacts. Service version is incorrect"; + + private AaiModelGenerator modelGenerator = new AaiModelGeneratorImpl(); + + @Override + public GenerationData generateArtifact(byte[] csarArchive, List input, + Map additionalParams) { + Path csarPath; + + try { + csarPath = createTempFile(csarArchive); + } catch (IOException e) { + log.error(ApplicationMsgs.TEMP_FILE_ERROR, e); + return createErrorData(e); + } + + try { + ArtifactGeneratorToscaParser.initWidgetConfiguration(); + ArtifactGeneratorToscaParser.initGroupFilterConfiguration(); + ISdcCsarHelper csarHelper = + SdcToscaParserFactory.getInstance().getSdcCsarHelper(csarPath.toAbsolutePath().toString()); + return generateAllArtifacts(validateServiceVersion(additionalParams), csarHelper); + } catch (Exception e) { + log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); + return createErrorData(e); + } finally { + FileUtils.deleteQuietly(csarPath.toFile()); + } + } + + private GenerationData createErrorData(Exception e) { + GenerationData generationData = new GenerationData(); + generationData.add(ArtifactType.AAI.name(), e.getMessage()); + return generationData; + } + + /** + * Generate model artifacts for the Service and its associated Resources. + * + * @param serviceVersion + * @param csarHelper + * interface to the TOSCA parser + * @return the generated Artifacts (containing XML models) + */ + private GenerationData generateAllArtifacts(final String serviceVersion, ISdcCsarHelper csarHelper) { + List serviceNodeTemplates = csarHelper.getServiceNodeTemplates(); + if (serviceNodeTemplates == null) { + throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_TOSCA); + } + + Service serviceModel = createServiceModel(serviceVersion, csarHelper.getServiceMetadataAllProperties()); + + MDC.put(MDC_PARAM_MODEL_INFO, serviceModel.getModelName() + "," + getArtifactLabel(serviceModel)); + + List resources = generateResourceModels(csarHelper, serviceNodeTemplates, serviceModel); + + // Generate the A&AI XML model for the Service. + final String serviceArtifact = modelGenerator.generateModelFor(serviceModel); + + // Build a Babel Artifact to be returned to the caller. + GenerationData generationData = new GenerationData(); + generationData.add(getServiceArtifact(serviceModel, serviceArtifact)); + + // For each Resource, generate the A&AI XML model and then create an additional Artifact for that model. + for (Resource resource : resources) { + generateResourceArtifact(generationData, resource); + for (Resource childResource : resource.getResources()) { + if (!(childResource instanceof ProvidingService)) { + generateResourceArtifact(generationData, childResource); + } + } + } + + return generationData; + } + + /** + * Create a Service from the provided metadata + * + * @param serviceVersion + * @param properties + * @return + */ + private Service createServiceModel(final String serviceVersion, Map properties) { + log.debug("Processing (TOSCA) Service object"); + Service serviceModel = new Service(); + serviceModel.setModelVersion(serviceVersion); + serviceModel.populateModelIdentificationInformation(properties); + return serviceModel; + } + + /** + * @param csarHelper + * @param serviceNodeTemplates + * @param serviceModel + * @return the generated Models + */ + private List generateResourceModels(ISdcCsarHelper csarHelper, List serviceNodeTemplates, + Service serviceModel) { + final List serviceGroups = csarHelper.getGroupsOfTopologyTemplate(); + final ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(csarHelper); + + List resources = new ArrayList<>(); + + for (NodeTemplate nodeTemplate : serviceNodeTemplates) { + if (nodeTemplate.getMetaData() != null) { + generateModelFromNodeTemplate(csarHelper, serviceModel, resources, serviceGroups, parser, nodeTemplate); + } else { + log.warn(ApplicationMsgs.MISSING_SERVICE_METADATA, nodeTemplate.getName()); + } + } + + return resources; + } + + private void generateModelFromNodeTemplate(ISdcCsarHelper csarHelper, Service serviceModel, + List resources, final List serviceGroups, ArtifactGeneratorToscaParser parser, + NodeTemplate nodeTemplate) { + String nodeTypeName = parser.normaliseNodeTypeName(nodeTemplate); + Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + if (model != null) { + if (nodeTemplate.getMetaData() != null) { + model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); + } + + parser.addRelatedModel(serviceModel, model); + if (model instanceof Resource) { + generateResourceModel(csarHelper, resources, parser, nodeTemplate, nodeTypeName); + } + } else { + for (Group group : serviceGroups) { + if (group.getMembers().contains(nodeTemplate.getName()) + && WidgetConfigurationUtil.isSupportedInstanceGroup(group.getType())) { + log.debug(String.format("Adding group %s (type %s) with members %s", group.getName(), + group.getType(), group.getMembers())); + + Resource groupModel = parser.createInstanceGroupModel( + parser.mergeProperties(group.getMetadata().getAllProperties(), group.getProperties())); + serviceModel.addResource(groupModel); + resources.add(groupModel); + } + } + } + } + + private void generateResourceModel(ISdcCsarHelper csarHelper, List resources, + ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate, String nodeTypeName) { + log.debug("Processing resource " + nodeTypeName + ": " + nodeTemplate.getMetaData().getValue("UUID")); + Model resourceModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + + Map serviceMetadata = nodeTemplate.getMetaData().getAllProperties(); + resourceModel.populateModelIdentificationInformation(serviceMetadata); + + parser.processResourceModels(resourceModel, csarHelper.getNodeTemplateChildren(nodeTemplate)); + + if (csarHelper.getServiceVfList() != null) { + parser.processVfModules(resources, resourceModel, nodeTemplate); + } + + if (parser.hasSubCategoryTunnelXConnect(serviceMetadata) && parser.hasAllottedResource(serviceMetadata)) { + resourceModel.addWidget(new TunnelXconnectWidget()); + } + + resources.addAll(parser.processInstanceGroups(resourceModel, nodeTemplate)); + resources.add((Resource) resourceModel); + } + + /** + * @param generationData + * @param resource + */ + private void generateResourceArtifact(GenerationData generationData, Resource resource) { + if (!isContained(generationData, getArtifactName(resource))) { + log.info(ApplicationMsgs.DISTRIBUTION_EVENT, "Generating resource model"); + generationData.add(getResourceArtifact(resource, modelGenerator.generateModelFor(resource))); + } + } + + private Path createTempFile(byte[] bytes) throws IOException { + log.debug("Creating temp file on file system for the csar"); + Path path = Files.createTempFile("temp", ".csar"); + Files.write(path, bytes); + return path; + } + + /** + * Create the artifact label for an AAI model. + * + * @param model + * @return the artifact label as String + */ + private String getArtifactLabel(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(hashCodeUuId(model.getModelNameVersionId())); + return (artifactName.toString()).replaceAll("[^a-zA-Z0-9 +]+", "-"); + } + + /** + * Method to generate the artifact name for an AAI model. + * + * @param model + * AAI artifact model + * @return Model artifact name + */ + private String getArtifactName(Model model) { + StringBuilder artifactName = new StringBuilder(ArtifactType.AAI.name()); + artifactName.append("-"); + + String truncatedArtifactName = truncateName(model.getModelName()); + artifactName.append(truncatedArtifactName); + + artifactName.append("-"); + artifactName.append(model.getModelType().name().toLowerCase()); + artifactName.append("-"); + artifactName.append(model.getModelVersion()); + + artifactName.append("."); + artifactName.append(GENERATOR_AAI_GENERATED_ARTIFACT_EXTENSION); + return artifactName.toString(); + } + + /** + * Create Resource artifact model from the AAI xml model string. + * + * @param resourceModel + * Model of the resource artifact + * @param aaiResourceModel + * AAI model as string + * @return Generated {@link Artifact} model for the resource + */ + private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { + final String resourceArtifactLabel = getArtifactLabel(resourceModel); + MDC.put(MDC_PARAM_MODEL_INFO, resourceModel.getModelName() + "," + resourceArtifactLabel); + final byte[] bytes = aaiResourceModel.getBytes(); + + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(bytes), GeneratorUtil.encode(bytes)); + artifact.setName(getArtifactName(resourceModel)); + artifact.setLabel(resourceArtifactLabel); + artifact.setDescription(ArtifactGeneratorToscaParser.getArtifactDescription(resourceModel)); + return artifact; + } + + /** + * @param generationData + * @param artifactName + * @return + */ + private boolean isContained(GenerationData generationData, final String artifactName) { + return generationData.getResultData().stream() + .anyMatch(artifact -> StringUtils.equals(artifact.getName(), artifactName)); + } + + /** + * Create Service artifact model from the AAI XML model. + * + * @param serviceModel + * Model of the service artifact + * @param aaiServiceModel + * AAI model as string + * @return Generated {@link Artifact} model for the service + */ + private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { + Artifact artifact = new Artifact(ArtifactType.MODEL_INVENTORY_PROFILE.name(), GroupType.DEPLOYMENT.name(), + GeneratorUtil.checkSum(aaiServiceModel.getBytes()), GeneratorUtil.encode(aaiServiceModel.getBytes())); + String serviceArtifactName = getArtifactName(serviceModel); + String serviceArtifactLabel = getArtifactLabel(serviceModel); + artifact.setName(serviceArtifactName); + artifact.setLabel(serviceArtifactLabel); + String description = ArtifactGeneratorToscaParser.getArtifactDescription(serviceModel); + artifact.setDescription(description); + return artifact; + } + + private int hashCodeUuId(String uuId) { + int hashcode = 0; + for (int i = 0; i < uuId.length(); i++) { + hashcode = 31 * hashcode + uuId.charAt(i); + } + return hashcode; + } + + private String truncateName(String name) { + String truncatedName = name; + if (name.length() >= 200) { + truncatedName = name.substring(0, 199); + } + return truncatedName; + } + + private String validateServiceVersion(Map additionalParams) { + String serviceVersion = additionalParams.get(AdditionalParams.SERVICE_VERSION.getName()); + if (serviceVersion == null) { + throw new IllegalArgumentException(GENERATOR_AAI_ERROR_MISSING_SERVICE_VERSION); + } else { + String versionRegex = "^[1-9]\\d*(\\.0)$"; + if (!(serviceVersion.matches(versionRegex))) { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_INVALID_SERVICE_VERSION)); + } + } + return serviceVersion; + } } diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index 3cbaa13..b7957f7 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -30,7 +30,6 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Properties; - import org.junit.Test; import org.mockito.Mockito; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; @@ -49,114 +48,114 @@ import org.onap.sdc.toscaparser.api.SubstitutionMappings; public class TestArtifactGeneratorToscaParser { - private static final String TEST_UUID = "1234"; - - /** - * Process an Allotted Resource that does not have a Providing Service. - */ - @Test(expected = IllegalArgumentException.class) - public void testMissingProvidingService() { - List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); - new ArtifactGeneratorToscaParser(null).processResourceModels(new AllotedResource(), nodeTemplateList); - } - - /** - * - * Add a CR (a type of Resource which is not a Providing Service) to a Resource Model. - */ - @Test(expected = IllegalArgumentException.class) - public void testAddResourceNotProvidingService() { - List nodeTemplateList = Collections.singletonList(buildNodeTemplate("testCR", "CR")); - final Resource dummyResource = new AllotedResource(); // Any Resource to which the CR can be added - new ArtifactGeneratorToscaParser(null).processResourceModels(dummyResource, nodeTemplateList); - } - - /** - * Process a dummy Group object for a Service Resource. - */ - @Test - public void testInstanceGroups() { - final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; - Properties props = new Properties(); - props.put("AAI.instance-group-types", instanceGroupType); - WidgetConfigurationUtil.setFilterConfig(props); - - ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); - SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); - - NodeTemplate serviceNodeTemplate = buildNodeTemplate("service", - "org.openecomp.resource.cr.a-collection-resource"); - serviceNodeTemplate.setSubMappingToscaTemplate(sm); - Mockito.when(helper.getNodeTemplateByName(serviceNodeTemplate.getName())).thenReturn(serviceNodeTemplate); - - ArrayList groups = new ArrayList<>(); - groups.add(buildGroup("group", instanceGroupType)); - Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate)).thenReturn(groups); - - ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); - List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNodeTemplate); - - assertThat(resources.size(), is(1)); - Resource resource = resources.get(0); - assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); - } - - /** - * Create a NodeTemplate for unit testing purposes. In production code this object would only be created by the - * sdc-tosca parser. - * - * @param name - * name of the NodeTemplate - * @param type - * type of the NodeTemplate - * @return a new NodeTemplate object - */ - private NodeTemplate buildNodeTemplate(String name, String type) { - LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); - nodeTemplateMap.put(name, buildMap("type", type)); - nodeTemplateMap.put(type, buildNodeTemplateCustomDefs()); - return new NodeTemplate(name, nodeTemplateMap, nodeTemplateMap, null, null); - } - - private LinkedHashMap buildNodeTemplateCustomDefs() { - LinkedHashMap customDefs = buildCustomDefs(); - customDefs.put("attributes", null); - customDefs.put("requirements", null); - customDefs.put("capabilities", null); - customDefs.put("artifacts", null); - return customDefs; - } - - private Group buildGroup(String name, String type) { - LinkedHashMap template = new LinkedHashMap<>(); - template.put("type", type); - template.put("metadata", new LinkedHashMap<>()); - template.put("properties", buildMap("UUID", TEST_UUID)); - LinkedHashMap customDefMap = buildMap(name, template); - customDefMap.put(type, buildGroupCustomDefs()); - return new Group(name, template, null, customDefMap); - } - - private LinkedHashMap buildGroupCustomDefs() { - LinkedHashMap customDefs = buildCustomDefs(); - customDefs.put("members", null); - return customDefs; - } - - private LinkedHashMap buildCustomDefs() { - LinkedHashMap customDefs = new LinkedHashMap<>(); - customDefs.put("derived_from", null); - customDefs.put("metadata", null); - customDefs.put("version", null); - customDefs.put("description", null); - customDefs.put("interfaces", null); - customDefs.put("properties", buildMap("UUID", buildMap("type", "java.lang.String"))); - return customDefs; - } - - private LinkedHashMap buildMap(String key, Object value) { - LinkedHashMap map = new LinkedHashMap<>(); - map.put(key, value); - return map; - } + private static final String TEST_UUID = "1234"; + + /** + * Process an Allotted Resource that does not have a Providing Service. + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingProvidingService() { + List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); + new ArtifactGeneratorToscaParser(null).processResourceModels(new AllotedResource(), nodeTemplateList); + } + + /** + * + * Add a CR (a type of Resource which is not a Providing Service) to a Resource Model. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddResourceNotProvidingService() { + List nodeTemplateList = Collections.singletonList(buildNodeTemplate("testCR", "CR")); + final Resource dummyResource = new AllotedResource(); // Any Resource to which the CR can be added + new ArtifactGeneratorToscaParser(null).processResourceModels(dummyResource, nodeTemplateList); + } + + /** + * Process a dummy Group object for a Service Resource. + */ + @Test + public void testInstanceGroups() { + final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; + Properties props = new Properties(); + props.put("AAI.instance-group-types", instanceGroupType); + WidgetConfigurationUtil.setFilterConfig(props); + + ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); + SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); + + NodeTemplate serviceNodeTemplate = + buildNodeTemplate("service", "org.openecomp.resource.cr.a-collection-resource"); + serviceNodeTemplate.setSubMappingToscaTemplate(sm); + Mockito.when(helper.getNodeTemplateByName(serviceNodeTemplate.getName())).thenReturn(serviceNodeTemplate); + + ArrayList groups = new ArrayList<>(); + groups.add(buildGroup("group", instanceGroupType)); + Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate)).thenReturn(groups); + + ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); + List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNodeTemplate); + + assertThat(resources.size(), is(1)); + Resource resource = resources.get(0); + assertThat(resource.getModelNameVersionId(), is(equalTo(TEST_UUID))); + } + + /** + * Create a NodeTemplate for unit testing purposes. In production code this object would only be created by the + * sdc-tosca parser. + * + * @param name + * name of the NodeTemplate + * @param type + * type of the NodeTemplate + * @return a new NodeTemplate object + */ + private NodeTemplate buildNodeTemplate(String name, String type) { + LinkedHashMap nodeTemplateMap = new LinkedHashMap<>(); + nodeTemplateMap.put(name, buildMap("type", type)); + nodeTemplateMap.put(type, buildNodeTemplateCustomDefs()); + return new NodeTemplate(name, nodeTemplateMap, nodeTemplateMap, null, null); + } + + private LinkedHashMap buildNodeTemplateCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("attributes", null); + customDefs.put("requirements", null); + customDefs.put("capabilities", null); + customDefs.put("artifacts", null); + return customDefs; + } + + private Group buildGroup(String name, String type) { + LinkedHashMap template = new LinkedHashMap<>(); + template.put("type", type); + template.put("metadata", new LinkedHashMap<>()); + template.put("properties", buildMap("UUID", TEST_UUID)); + LinkedHashMap customDefMap = buildMap(name, template); + customDefMap.put(type, buildGroupCustomDefs()); + return new Group(name, template, null, customDefMap); + } + + private LinkedHashMap buildGroupCustomDefs() { + LinkedHashMap customDefs = buildCustomDefs(); + customDefs.put("members", null); + return customDefs; + } + + private LinkedHashMap buildCustomDefs() { + LinkedHashMap customDefs = new LinkedHashMap<>(); + customDefs.put("derived_from", null); + customDefs.put("metadata", null); + customDefs.put("version", null); + customDefs.put("description", null); + customDefs.put("interfaces", null); + customDefs.put("properties", buildMap("UUID", buildMap("type", "java.lang.String"))); + return customDefs; + } + + private LinkedHashMap buildMap(String key, Object value) { + LinkedHashMap map = new LinkedHashMap<>(); + map.put(key, value); + return map; + } } diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 297bb4d..84a2934 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -191,6 +191,16 @@ public class CsarToXmlConverterTest { assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.PORT_MIRROR_CSAR); } + @Test + public void generateXmlFromServiceProxyCsar() + throws CsarConverterException, IOException, XmlArtifactGenerationException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-Grouping Service for Test-service-1.0.xml"); + filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml"); + filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SERVICE_PROXY_CSAR_FILE); + } + public Matcher matches(final String expected) { return new BaseMatcher() { protected String theExpected = expected; diff --git a/src/test/java/org/onap/aai/babel/testdata/CsarTest.java b/src/test/java/org/onap/aai/babel/testdata/CsarTest.java index 0def88b..4f4c8ad 100644 --- a/src/test/java/org/onap/aai/babel/testdata/CsarTest.java +++ b/src/test/java/org/onap/aai/babel/testdata/CsarTest.java @@ -43,8 +43,8 @@ public enum CsarTest { NO_YAML_FILES("noYmlFilesArchive.zip"), PORT_MIRROR_CSAR("service_PortMirror.csar"), MULTIPLE_VNF_CSAR("catalog_csar_too_many_vnfConfigurations.csar"), - NETWORK_COLLECTION_CSAR_FILE("service_NetworkCollection.csar"); - + NETWORK_COLLECTION_CSAR_FILE("service_NetworkCollection.csar"), + SERVICE_PROXY_CSAR_FILE("service-S1-csar.csar"); // @formatter:on private String filename; diff --git a/src/test/resources/compressedArtifacts/service-S1-csar.csar b/src/test/resources/compressedArtifacts/service-S1-csar.csar new file mode 100644 index 0000000000000000000000000000000000000000..9bd988925d90695044396b7296603eb80992fa3b GIT binary patch literal 67874 zcmZ_#Wl-JT8#at?+}(ZSE=4!)?heI^7k77;;uLo;S}5-B?(XjH@bde=XP$GO4=0(- ztW0LIvXZR(l3Y=eg@D8az{0`;Y!&U~0slV>+*jM!$;gq(#?;ve!PQ{_=^Dk=b?#oC zQF_j0bWD04NtTgrVr;Tbg=LOy3qF+IURr*1Y?J|P1d}{T3y=%|D9OUWR%hpcIKIv# z_O&Vf|LqI!>!_Nds;Drd{8witM$Sge|J(gv-|{hkk$YGW#oYYFeYXHEj;$&E@ z>d~0A4&{9!dzZsY*Vz|YwjPjz+gZt1*ciJ}vcEDwY%3T~Ec~%1kOrwqo9?y~?sS&r zRB<$L-Rl3?+pef(=9tvXk&Q}qI~+_A$>|RH@9|b0!Bc7L*sO|vICX;gVwfuHu52{l zD28psfcyP77S_WnrjV~IE&o4b0SCYWUW2^kH|H_ghco~Hln4X>`PWe~Q!`6jOJ_?v zTPJ2GQ%6@zV^c=eZ;Z~SHuly=&ZbNrHrD4pda46IJ^sL_iH}aI`!w@ z5dW=*j&jy$F+fKCP)C!GZ=q0ky|3Psi%gJ;mo`g48%D|EZTfYQkQ7f5gcgrRtx%}E z!&Cn2aVyOCMAE3%^I=EqZS+(;-6!d3Rd1_OKhvhBMQ{D1_Flz&w{YUr02@Kj+x5pL z+^pTEK$}myRNK?CTf1h7VCTx^`>!Yc=iRs1yt;dR=8eH=-@~xjsMp?-s4yCR@B0Kb zZ{uA`+q26VYc4^*4@^H91J@nNXSaU8R)rIOiR;nRero1-w0=LvkEiNS%8%^Pmvo=^ z-jYx6=GM=b{nd=>&5kx+zZXjQJ>-tJ=kwbz#ml-=X_db=oAG2Qo$X#&plV%eh4$Oa zjpk|B$KmBjo2XXI%YDi0w^c9ZyB~iG#{0vJ9&&ODy4?B|{NB}l1-&13Y1TZAUZ~(z zFU{#(6W;l>e6F+G?JwJ3)-f8tdB2JC1<)uYE_Swk4~}o5y;)$!M(J4dc?jDdk)I!3sOeLk$Jb6W5PW;;F8_jeu|{Avfb!VM>O()%>t34iQMJ|oI*^1M-gx>Hxb zitJt6J!@vNM9=iL^xI*5Wcpo~iptcZjih*92km`x z?N7O3_KPXVVcwUW?2H>-8)nX27}bbZGs1-`RN|IQM)g zT>3O)wq10g;(*rd2l_DBfm>JC>)(eWss2F($8`{#-bnjMF>l&1F<}^iPjr@R=5@V7 zQ^mPh=;xwbWcOpa<*@h>wT~cM!WNT;^LI;YM+&2z;d=Wk!^g*h*Hk_9Px`4J2fS-D zbrFIa(_`tQ{NbQ$i`0~;&+B{I&(md2=C?h=>d%r52vEQCNz|z$)bk(H{?d+-nbC9A zYi5-R!C216oFDHrZ0?M9&f@*MhV@P}CHj{#b*ht%P0#uV7PRR+PAe7twPf@|!* z!MwE}x3;&pUHIRW+p*I;k+!x&{4m1;zV7Serhwna?dW39ZdTZ*U5AJmOiB5>kAG9{ z^R^t<&s1GN9KrB$A7DJamxz=Q`Tg&qH4_9xA6PiV*e;+<8nm_RFDQ&7kW%m-@-3%< zcQT7H=8if{&o7rbb3ZYKBAi&03pDO_wP{#AIiAROVO|zH#vY+ewK3F{@oOYNSLq+IO;<2AqyO!Tq8+w7T#>;1j0gKrHolAQb!4u5y=)!jF zG0}WOzl3brp9NTCso&srL^h7xrTuZab*@Or1dhDVavaGSaZwMk$q18B*~7yzQE87P z;8C(sNxV{|gI;oh8NMpW&;oEIc7q56rXu(|M0=vBm(IW36tvO$c*EULFM%7vZEUnF z=IWXL>2NbvB9Otb zP9mCf$SIHw;KJZ=4d8W@Xyk?s6yT7Mu($B2aS}qo{;*(Z;AD_uu;HjxwlDuMAmcNT zY``wTMF8WdLLH+MoGW2z%VWg|L%f~0{k__qCMXLiA+}M6HK+=DACIa@g*`e=ioQny zsbGDG{O^3Qcs%H=ndkhesOI7&YBqWHs-!MI7%|096q@-pgCSw%P*a^zLDF?q&%Iem8b>kLG{I<=RVEl7`8V)5klBx4nzIx_Ovmx&S+{F$B@HE z&2KaGOT;11_v*cI|55NlVEuBDL?llUMWH(YpXvE6W8lxqza?`r`Cu8?v`oWcYCySBF=J=lp21SvmpPuyU3<5jH_Cc;vQXFhfphLa1%yd@S1qiR0( zMsVq`@sd4FIuv!k(pbF;6QpJjtf2fE4F2Vaw8PbB7ru$wN$M;5=6XN-Z!Qjb&mEN< zFcW|Z7Khvi6OK~FBhWM!1!OM5xgK&|5sAh7-haLI{Wy1W>(i*e ze5=BkQ@IAy;r?#rUp9obOu2um+it zfvb05NgN{r_nMa5nkwCacV8bRurLVgisS0G;p#T>3$LcNu7|_Ay>jWyF$Xq{=6kM% zk?y}I$46zpnDpf#6o-G~ACB4DM9MC|lX?5fpvaU$Yb&g`GMzeFoaCB74+^%32BPJv87JO71pc2agj%~## znUYYheCMu2YzC~VJHl|lZ=XDRJ7I+`HvljUF%)Z}cwm$kb2x6Ogjasme z#>f!Hp9mM3*Z$wVKgyU*f<0MuMIG`9S^&p7@8k1hm`j$9pe~V~M85k z7~y!M-|5>wAf8#``I%A88 zY`rGh2~!Y1aX%p3kRyEn%v-MkO@JNnM8B!+h#G(qSPytG6f6QNc5Z#0$h&_q7#W1U zAdoXvwHo@rssW2S_QfNOs1JWDKtjR7kMV*eL2dB!pK!$YFh?z!c)piC#Slm=VveCjffjxE8#_2+Mem$-0H z2jz#xNgF{(*>2d(D9%yPq;s$RvHh_S8*-%Cn4F-0G@A*jH34K=5us9`|Kg=yU>8sd zlavJ(JsDPwj4DLU{VMoM3nN*HP5LD9>dKgg511H`3Y~^%!WuU$L#Fmt z!z>NP>~GE$h9iaaosa{T0sd+Ma)U>c`spl2pw(m^j4?H3Q(%lRtU9!M;cni%xPaM> zBmfY+df>Q9;xKZt^%v!VP~Wow6riUj=nFsv1Kq@gUC;(Wu=oOYVf;}50sw1Co*;y7 z$KaSFWP{s8{P#7=!Ogkq5}`gI7Y{1SY-8uCE+a~jr}`w`Gd%u=pPxmom_S4 z)%y+6ulC-@-xs#DqQxf9d)&vCqUbHyy#IG`y+-B-g-rF>4^F1;0#^H09RFW+g6w2;# zDo`|$iq&J;?W!Gfao4;xjIm|Uu-^fN8Z*GqY2>a;jKg!356A8+<>^tTLK$lIK?7RJ zK!7EzYp|Ul?Lip4KvH-OUz!aGWL8F-eZvn0Oqqr9n}Y;!zm1|Vh86#kyQG!(0G%oM=58dT__2t zU08+pN8O#iAJyrWY<1x!>FK2hS3ScBNQjfnkYbVZt^>VxHjIz@oLD(eqj+-1)2329wr z=DMLOfDBEnDrQV9s-DAvW`hujDB1Sh*>Ioq{ile{>Y`zbPAD$Uz(!FTE`KtGA6Q(k zBYb8!t&G-++|Vd{;Y(4uJ>tM1Jc1uxJ)}XdgY>(R4F~?FC>(K&9KxB0oKuLK01O;F zSo;^#7{C^TSWgQf-CD{Yz_Ec}*R)?iE|en~q0fD180c^*H$a># zW5;>`}!Q81AL2q+HNzOW-9VdrpUOvq4J#Ofl5B32X)Jtl&4&=R72onqfQi~u@m zv>EJeJ!oI>`tzZW9~8_iiLhn2|H`n;MJSlGU(51(`vpP%ir@m=^*oEZhKXr;l8wEH z$_eAr@BqqX37CMiAtpAoS_*(PGEBmpY4E7jv!T61;fc+qKfaJYZYdZ=SUh3&iFcrc zJL!M;>W}k3uoVIe=uz)MC{Ox8su;+2Zx$#K%>fq0DR5V;PMDmKu*{vj5qO>N7o@(4ve?27KRSyxqLY|M*C= zWXIM>H4n4?F-baim z)<*__p$6rqKqvu#&;c+2;w`#sfHtC6rhG&aWDnT5{TAPMnV(Zqhe+fc-_G-TrIeiI zl#(iVLXjpbbGJeWxXnQGME6%rpC322b@#nSdX!o3sUxF2ja#|Xis~_v5Y1(zJhpK^ zMXkqIoPyK{RBWW`N#{e=RvDh>6aLd~-8BQ9E#x9bI`&8{Lt{$4pQaF0wa}Y z=Y?h@M57!V7sIQODU4bp`h0U%_QiAT(~q?-s%^E@P$!WuB9kx*LH*{?n3juV^2@&q zWY|UXCekq?j>l^)ILPnoe2?7$vXq?5cNwHP@>< zo<@<*mu8Ziu}T&xG@A)9F44gsN`yEijVp6XF6%Oj#1RIz;4X4Wf{Qrj=sbN0Uyu^E z#1hYtD4eyUaq+`Pop;`EfY$6ZFKi90%DmA){MZc}J23sTdc&DU=ap_*T@eP-ylUh0 ze-<%$b@)xfucv%0bsq1;$NL*MemizbXhnU;cLp-Dn0j*p5m^30{P>nnyUWtV(p1OZ zgzEzq#0xK%VY`bK`VRDo93bCQoOTaJq+=~jh1W7L?msgWgQKY>lM;%@`jS$?^hIEI z1w>1qsNkto@U_Y!DsKL=Fih<^yRHR3so)tKco`eljD5I)*PL<3H=)2ult-ivWWw$E z!$DHwuqS#7TwZo+31xTfEe1A(3X%Copb`W5gxvkF0NY?aK~{7_DJo)vMq`Enl2TLD6kirg;qHTSRZh0fBnMoV zSGtVQ@K^yE3q8o!B`GsK+xyIEZz2&9H42FR4VpCC9vg`TwhO3x4#EdOAFjME4qP{) zsNQ{aM_|n1PQ8ZFL3n;D{S+!QN{|Y^=|b?wL?gXBe9X8AhpTxd)%0%yIDj?ztsV!( zi3s@vUVweDQl!AI`elEf*TdA43J`6N+Og2L6BaDzq_pa72P}X8@iUreP}m z5EazkNl+D(A@pnWCF2TKOCWS>XV8&6F0(F+*J*<H;#;{MBBS~yI(kbgY4JL2W zq#dOyM!lWXvkYfUcv4~RjcFS<J`nM;kYcKgun;!F$ScT$7cz_JtzbXx*j2FQ{2OCCD1R4r2$yb1I7yGqH8J?4pZ)kG%Q@By#w-{O#mwH#3(X7e6#NnKvEn#; z)BB@A>dVx;z_XBdBfcOr8VzR33CyOSj_>F0#q7K9J1Hjq{T8No@pi&wr)9-YDL>CN0SDFV zxp)ELDU)mgux~sw)KWP>pTN7}q-BNV5G?yFGFBlYsQ540AqE|eSjEk%6fLKW;w$+@ z^KXt2Q5RvND=-wE2g|uWJ}V)q3ct*CNNnR4x11iaCca4LDtdZY=)lVh%WM3Y$*qX z?2flTyw#!Q@MLbG(J&19oWoQ0&Yi@A13_>O31*oT=Y@62BM;zJ8T}c>C^DU=nbxl}w=B==Ib>U*+${Z!>ct_^SJOmh5 z*z;^^lS=$-V2l6i51mI5No9oeVBWc5F{W~Y{sTf?BThXh*ir&K$e)VJ2`2|ReN-xg zD0rYpgiXOAe$?@GNt9z0>W7VRUW^cZqZ@drv0bj5uNi|$vHQXU&M1!d}%A(AwPaRq1ZoJB! zrdL-GgW#qt*HghajY8v4;FtWc#rn|dAKtyDD_%ah$K^0=nm!FtMZ%ILQ(7Cc1n>V< zK~knYo}v&{@^5d&yCr4keHcLWt&_fM3cOzJsb*^) zqW6K1QuaPu-bv!G2c=Y9Zj0?pD1Etxd=x)Kz{wFs~C$7Ul zD`tM&b&0Q*XiqE6mUsbJ;Pgt+cZd~{p(JSYZDe#~`ay1kXB_T7$Mk#8IG)a=pU!xi zTpwxzW<#l|vxdJlb3XvHyJ)`U)&)#-=ubpMf4UJ_Oyadpb_!NBuD@)bq#`L`rrWP( z9C`eIy@}!Midfv1Ko(R)6IjT@xympL)M;S8a1d3+ug9}i;t{@Zc%=j3G#Jxtk0cLF z4IhifnQbXmV0A|TNm_sj2Py2s^(;w-@RVllHvK<}Jfdr5+?Ggg-0z4Bn9%EiA zGf)al5@~SSzybcU@$>ysYOU{tz4nN`-w+2`{%f=r8!y2Hy5Qje{6m^@nE?n6U_0P_ zP~ixuIC4lH$*QSR&|?0Ts9;Cp00UV$kVJ)6A^7)QQS$2hekx24`fkH_; z@%GlK+a&9p-%zVA&?tibX}gbrxkK)mfV}#{N!QX{La-g*kqiH4A2T4TAUFyW8gMA} z0~qm*I$E2QJv@`~zarJe2V@bNfcP>_0?T0${;1-P9hAnHz>98$`QeSRzKVLU6RWCJ z*C3Yqan1>{d?Kd$1&8>*#UkJMF>DViC;eanPBc?NpM!9fPLR#O^QmIf{6(?%6|o7$ zo^J8=cP}37N9;$^6`sFb^Qbu-yV6WyE~YTP%3g@Jn`auxms(_f?IoM09gpUn`aA#a z0;cn~Zz0dEPs^Z$Kg#*7kAfjwSQ6~sZGO@!cDxk?)jY{e9kVG02AAa%b-h0(^)qP} zh6QJIha%Rg1@G7X?O1EUK8z4$rU0cPoB_kcf#X%UL&pz};1F<#6kW&wUR@jV68&bP zZm>XzxmGj+0CdCMz}oo4+?dn_ite4g6q31o%NvSe&{y&s@LMTQNnL z;?eQ8IsTA_r?iMr3gLt}!sepWgy#ZTxl6_3@qYA)q3#1&*(h`bV+ypmep|zu?CIQG zH!aoV1&2TOA=l;4DS+lyQd+zz2_%nVG1WSvxk^F=MqS`Pl0zx^74rtN>$FG2+bHDY z>_q{Vh$Zp(|3E)czqiG?k;8&HxrnS(8u(P0tSOUaU~zk1bH7d_cMx=}vOg#085CY% z0$EDay4@ac*Q+2k-x2s%_nXH^!Aj>h_*`A6O75uIp%6~!f zi$CU#EpS95%+*9_d>Z9^3a@V^BGc;5N$Xo6RyPi=L|nMAdnh5IBTH)M%94H17N8pp z-xif9cN_XY<79aN28Uekws0bNh6{H&NJ?B#rF)1tDU7Ao9Pyqs`YcUPyfi^?sN2(} zKrv3=fA%6vKNX0J2^Z?IBNT5c^cze&lu2re*T z=`Awk5krQ6Vu1n{^~WA1T)yO;Z%?b6z+KKk*3N%}Kz;|^?M?}{Z<}p7iXcvrLW3N* z^HMNMUVLK+AQj#f?hS3z*_>2;>VVMpU3LZ9s4#Bq2s?c-7_Zg}XQrDzsaMHVR*we< z2M-?fAAe=C*OQf|a-NCS-T&68X3UEc+88W;ylq68P9N62jWdTyI>x}p5HJqA`!;FA z=Nw1roKz(A>2d#Y-@9WpulHFa10EB{MZfE5&`V`rXspUf*Y*zOHE1cvidp30RNLkf zXjTxU@HtJP97z_FobK1PuHqmL;KMs4!^fM90AFDq%SePs41qDw`QTQxQzn>m6bPcz zLyncd3vkW8AU2{kM}8oPD1eB2DV|*XhcIOyyfod!6nUXEyFf}xmv73T(^CKGzo>~B zY1KiW6;Gq@YLV2E28Wjl^wGk%i=*{w`B#6Rh%ZHlfw_LtgDHn@f0MqLn!z!_EW#YM?P^nb_nVr zD?wqimF@HFwn`k{Zq_dgAeWehP=%vmgEP_~7ikI_&yY`C`VWT6TxQ2cI7aSn7@`wB z{1+)1S0@8X^>{*Qd?msM!qwo}h_UnYDqL76gN62XjId~QSihC)>N;CPVBDmb( zd`>r$du<-R5|Sx8%rgOv6ghsYyHpO1lpD76i;P866sEiJoGuuY>RzG$Tl0fh5GN*) z#@+}=(agwRY9qx&Ly9zY<^Vx(5J)R57iZih58>S67$+oh%s~97XSn=#6Z17e-QENi zX(3BqJ5oAc*zQH7US@h0@IK6;GcHP~?tkDhc|nhvxP8dyQBx+f5SaNTR&Ehpv%+{u zdCn1{5b^BnMVa+bXrQEg(Q@7?)u{v--S5-rCXZ;*?3gfUzQFj!F{(&0VzRlF1VE2E zD77HS!Obg=BH)RD4`D1c!NUuB$(JZ4B9%bQB(cKV(u`Q?%70Olz@tv1x)>z|fe^&{#vIl=4%M7#xE} zN^c7ugm)*R4ynmf4fYyv=Y+6GJwsMQHk2hS&92bqfLR1O6Jbk&jiCC;W&ytw_gi0O zDhfzAZCWcL<|yx(z{kDAdw7`hC_8-!I#EoTT^&1jMQ7k4500UqhjS9DX37t4EetxK zEzZw~P9xZ4=ZIzx9l^MA>g5qpPv;Hox<`SsWLzSPkAXa<73NU~pLs@0Nt&fsKo#3* zM@dQ08Ufp0AL#0V`q9Z@I>aqi-@z81nV z9Z50?#|iHQa)xB6VSgkD$g$f`7)&$y6d1V6D9%`~Jc`xyT&tu}+Q3wiRX4;U;DciB zTnM%)Sr;vCrpHBuHLF@|P_56DLKZfJ07tpBL^Z$d8N=0uCy@E`GFD+t1C$+p5B4B{ zYvQ7=UqjUp)eyBvyIa7sLp4R6_Uom7kH=buU&OM5uNuUT}+_QFwDKQBqdB;`GaniI2x)I=8PzZpu2BMz5L4!wP>JE zl)p0!4*`U=?`}q>lB{B#>6Ki}UzG02!14Ggryf_H{;b`_EWzUsDEq@f7E2=!B_Q64G{HAP8^(o~^| z7ewSbAMm#$FP-ye!pd*7x~rH)+j}#$rJ^=^fJVK?IM8~-<5Kpwow-BebRDqP9$I*1!OrwYP zqE;OlAKBlEJ^_PZVUPriABoOgez6da7hd+*Y^ zQRJ!zeYoLoMYmzF7xH%DZeYT!=5q{BG)fu=k~94){C=cVEVSiN#Z6Tv3FKg%n4~3x zsTUw)(%S>{FyYFXABPYOqy2WZKKD%d^!`$6p_WUM@$R-zV?Oz2bZZGal$8ZESDA5! z!Rp_9#TQZaYeE;KXYEaX!#%zvsOPl;m{CRWDeW6R_Fy=v)H+Y);8dihFhp+zGk?X& zr7tKA*SQ`}33vJBj@A#VQp1utJobO&5>3Uh{b7%sG2Qt@$_S7;?nx0MH>5_qj;9gJ zQ;Kb1I}-n;7?BV&PZdW#9!V&j@dtYYQbUwv0ieZG>$*Q+wu@b&d~o}xvI`U)5c|(X zgd;NFK9g}5D141CX^ai`$%?4rd8{oP8a4ZLu?%`=Ioy%8hk}U?HB; ztjj{Cgi~OMZ~{(+OnHp+r!rOP?4IHcUzO8a&!+Udq7Qb~!#{^$=$d<+Gn|^ca9mk; zdQ?YY*|GF^VZaK$VJaHWM4N#QTWmlJTWxMGU6`*$2tDP1pcuVULP3HdBD9)l#$mP5 z)x(kYhnh5S&oB46K3@GfIQ9sh=ZF7(@Ag?_oscH~KX3s|0nEOV;Jy+LZqq_pf>)jC z6?mPwyRTr0>ZA^ogk+nD%U=?B*Kuw&w?@eg%y_$A%&7>7qlJ~(g539^D30-bs|r#f z@i?CTIn_I%Q=$5J6R}ctozmVstP^Ffs(j=b6eM8`3tl%A@Pu;7;d%QFTKcyFsulOh zgn!27n;A56M1l-7tn6Pb;m(h8U&MN&B2SuCrI@54*9F-nr*Uq@_8**Yq4D>o=1nb?N8t5k~n6brQN zT*b#)#;H^>Ei_H3Q|1pMZvN?@@R~rF5Rs!?Dk8;-|evVAc~*PE*7~gKM-4<&)Lo_EXf`$HeBt_g~OsuyRGn!H*Zb zB+-zZa!PgRA#|DifIe|CD0yY2Uuo>$CsIqR&E^X6h(4Q&4R`QrF$a-f^f(45@%Crc+2#?b|+ z4VTK7G13RtAzJ?-rp*f2$3vjbQq^>p(2A(3#R>`P*_jVZZidTDYE}*DL7GFwIHIz) zsSTBuOdw`Kqme2JBpHJp3kNG=aQ|)3w*bf@{S}yKw)$omOj+uGKnm9qpNB7>RRU!7 zkNnq^lKuiRtg@*7NvHyi0geO~K&jSOlWVv@$m-6$UEBKohwRQ2T%jKyyM|ZZlNRd= ze%xkEK{<~M&%)6srl6Bb9_1sm^odQFk9;r|x=hE0`AGF+OX**ZWyKdJGF#*PCAp9I z0}+KY0l2!8Vyxdkdi7k8V3k)xr58sHz!9KLo9&tJ5&My_P0(vr>Lnh*b~eftyGU=L z*N@oh;cRcyjTzYaMVOi7!~XkZsSo@hfCm#*hQ>eT;*7x$%yeO{gs3*2UGe$2HJbl< z`lkDAl)z*97lvcRTz388;yVVl9fHbDky?UW3I)M?VE1psy+5pucsd<;vu6JwR^xMf ztqLWR0qA0x0xP7Vz|mlN<(32FX#6_o38bVCT&M8GGUHxYHAq8!(?XOfXiPEl8Pt5P zCW3WzN@u7RGMD{S90_UoDhY=hwqCDEccs7wL@s+sU6-aWpc>5)+Mk3^u9jB_<~f?@ z+UEWe4LZ!6VN!_tVxC_D6xV`OsmrR83z)`d#h8Zj?Kl|k1p~-@B~x3YuuEfK`CSD9 zlB>v;cowK|SQ~|G#BfJQo{>lCxq!NhseT!o!Zzh>;>(S`#Bj;Eb55WkjWJniVyM{8 z3}s5IKf@CVJ_#p1Kz1x-XBfFvv$Bmjg>G^q+ykliZ42kxRdHh>Dho((8}MITYogf6Swgwk+3|U5By}0VY2)`aIw*MqrH2t^0n|o5XSBC& zz#pWo;{r>Pedd;wNU6p@mVH+C=iI(y83S#{uJ8(D#!a50V4OBIA>Ya|YNWfUjG%e% z#9Ej1sV=}a!RHD!y^CNJigleZdL1yC(s zso^wK{-SD&vAYY7dF_dS64xQBz;!fUte~2796(3{JIpUIR{k~J6^?cXw*y5MF$M?~ z*3}3fJxP-^l$c^E9^lR;fQ?8`?-^HfV*gG=UlVaV_rSg)E`0Y?E7N|iurs>PxxWi~ zC+&#Rz@IvfP#~220BW^0sa>yxKFYK|x8ZcR} zZ~MKEt<{$!n?nMY&CvBm%@{Hle4Yr;mH#EIeZAYl3kOu%-5z51@6jidH>!FNRn#<^ z1+xYnBG6B7Abm-8HVmw_KSM`JX4cIu?H{48$I~~(nEalK-oi2lgp1I*v%{v`-C{Kx zhN_~^I1gKAW-P?1cD(%(Q^Y_UBWU^e$mN$k-4s@OpI_3SIkWXs&c4hAU!BhGajvZQ zJGpAb8+e#fWx*vKwJ>jUc>tnWWsY)b?VE6-v5Ez2`#fe|Z1BIL6}R+DUt;Ded;NJI z(Ni!QHc!!59MB%YpHSfk&d;!rXm9@)2_;W38}DJE|VCikr-m57@nn2bdp_3W$-F1jO^9Jo&UU!L!Ym-XfB zLCZE9$o46>Sq+;S&*$RnjW%oQM%9Wok!Fu~i+*oE?~92WmwiS5`-B&_Ii{N4yL{kC z{FCl5;ik%Er+gtPtv(Sh!F++e&DT+yv?clJJe!+H7N(?*DknxRbeXKUZJ;pm+IXzz zaSWM$!Xds+oGzBh<$FMe(%NzS64ovvq^3YI<16Lm9a4e;VKPcS`_yF?tI&%n@ca7- z0qz##g-Y|;4`$`2iuTbzXZJyMG1SM2E$01Y<9n2w<=X+{z7}2^ujxqge}j`v8nFwx6TtXSi>mHtVk+b%v>mKH0ez%I{M2r~M|= zIgsR?rD&z!Tp>*`cC1b>${W#S>`_uo#2YZ^5?^xX|FzmFE$XhD&T!`t%739lrGh#~;BW)uVN9AuV4nT&v z0<$kzCh8DAsOh;e*dE%i1+))(LgmFl_P6J0$P*&j;?tMJ`F?DGXKO3Nz*W9 znfkvAc#&zlzqE=4|>!qmoTE3&}v~QvN3j>p8a5TTOkE&=iPw0k< zBuu-2YFS!B=Ix?Y@wkIV0n7GR1;D){6B_?P7Un>?4YF9ol?~KJ1#B3W7HcX`vF18c zcuWS8AW^ppIJ*?F*wG8f@PFN#KxVW0uY6WQbo6>w06|FJp_%_iG-L7G@w0qHL09XW z^lRV=Dbm&ib%mu-RJJ)%bc}#0vn3F>?eon9epf-SJc2j$MXp7c0Iff zr@sx7b}1)B%UE=f*{Mx`_3u})vo586fffEjEVn;SBCn2*4y2OZ>YlK~I~E!5`-tKX zvnKJ4bm1g~6i9P2UTekMy`Yb=6Y?^w@1BHTPJBdkzvH3qt&5JKh+8901FaNMF988n z!!R%Msx{F4tZ=m5pw_yfG`&BDO?tn5y!g3!js5@Y+C;V^IZ zrQz798xIF|jBXJdAl+ zp2CB(fR*gElh9+KpR#OjOO>_}tK~(lSyL@djepQs068dH3K`Z`qffL6o{bh@utmq` zJf!%B@#?>{fp{cX;v;j?f`cP*DhNTLSVns}nf4j0LqnCtkcd^*9zbtT4?u8v4$at|wg7>qFhhk{$kAXk(6P#rjI{(X)f=8s#d%}HOP%5 z-fi-mx2pfeG1-HJi}#dOVipUZoi`-2&G`^e^IyQ<>^BE@iXsYAF>7l!+#fqL#L!my z=jwKnSdSZeaBu2~f7Jb;X9|o@3Rr1$NhYdWosiKhU zFF7{l`Sri!UzUaGJo4BV=$h+ZU52KMHya4nf1B5T|Atn19ODs6e_1eggioTr=pH36 ztdO3&o5u$*q;nbgs*JYYrxoC`)YE1Sof+NBA0Q*h9mLv|5?{ko)Bl2vt9#PRoU6Mm z!+8{)$DewpNHyvGw_lV}o{2$#^gKGam>x?ua1`7l=rGDmfD`8sF55>k$z2ST_*;Pjyy9rAIFWXWD&>a+qC{%`Zxi$(9x=rd_LkB(!O{D`&??O zWQ$uhX8gwgskz9Wno1^GR;qI|i%k-(xBG5%}s~dg`#r! zdEj-s4&zlJ)%W#b``n85S$(Jf#1wlFoU)(C-lD%4);5Lf}C8R8s4`B zQvq9s_iGB8aBoLJGVA86^<&3p%Ekzi zo1Bi6O&unJ_Curfmz?a(l7HnT@0~niqBH#?_D4tgF8t@$E&8Sj2J4uFHvQuo9bR{} z_X0OFqUjqY26M4z?)d8!F)`*YO&Ob?POj4EqP0zakNdA-T$lRm?H;!mA0Kzyy#mfC zOi$;oAZx83I83>!NgE?+?2JN3WhmDhZ*>8;ZiJH(BL^&Uo-LswhJ}-@~*&pv= zk2x0~Pglq7Dp0l=YKAya9bPLLC+MHMpLd*$v7b#UX2W}FEU)nwPGi3Bj;n-}B%Nvm z1Y83RHaH7c6(3&AuDc~37Jo+6Nf}Uj!_~pT!uW6oNVrg{0v)=aX`t^$oZt{F> z!H@s!wUHX{YX(XbbAIo9GVd5VOugO4qS$>*?(D9Yv_4*T=+a!3w8onIRP#l}NW7>w zKDLVgwSC%1g?ZjTF!y?be_nMgxNmrS=u}sf+P~=MW=88=H$NJwtQ&2xe@f#^tUkFg z>geBp^&Wj*z7bUR=7CwrO2=eA8?R-QL+t(obvyMT}EK4sY@K?`H2%7{>4mMy0q%PZWl*4SCOur ze;BS%-1(u$1e&&QdrRHOA?~S^u5Rn`A7(T&1jYCHE=)9IcE4_oi8bi01RWN##^-j{ zz2WG||H-FY>owD_Sb8+#gouN0OkI$0#ZoZxxRLy>YtWf%`$lOwQYa<0vKqn;M>&In^Vy7+0b~%Q>a1W0X8_aP3VvBfpCKv3<@40( zvE1Xdk<>$+02^QHX0l)oRO^wsD0x9PuOe9x6sip1b0FFD{gn^~7X^$S6<|3I>D_%p>h_Vi zVCpDmWX}aVF?wY$WGPazp;jup!H#e(7Hi1Vk-FfLJrLllEy8mtx`EX4Hnlb`<7a(; zE{h%O!NDnPx%{I#nSPAK`g`qBgfalyZbz0hfR$2@gkV)+fZ!rvZj`*foq#{yF zb{G}u+N3FM1+#+kEV{~?HLQw|yXSCRM@*#zX~}8L6C96uRDmqGbEsUwXXT#WT70AM zCLc#)Mr-KJ0&ZSYZ5bO~vRmdMJ0hbA@$ITw0bZ*SHE)&480##1;?CrD8+wVzH3!LA zaBeO}{n+IXZKYI@cD371bkjX&fDdyyvE%sfpR-s)IRo3j9P9VcGhmDKYM~fs)&NF7 zBuS?gmgQ94C*L{(eTw`9fJWb*oOrEpXg!T|DeC zBKCb3Tj$t^q7{NZV3dR|cWt-pe+hTA7m8h7Y7%gG%sqcA;rxFHd&eNlnkH(rZQHhO z+qP{_+qT`)?rGb$ZQFMDwDI*k@!oqQzCSlI_MaW+#M!5IW!73NE3?=_U{y`X#Udk8$sF@O-tWMpP3=>Igtpz=_Zd^^B~x=kdsoOm`wYlt7w$!mJhB1SZdI1J3dpL zXIZMEw4 zzf{q;N!PW#OO}ncQb0txTc##}Hijfw^v#vwjo1d|L(fEi z$L;BOThvVrr9^p_sIJQNlSaFXj})|wopqC6s@m7WG!53pbF7c*yXyA)e{%k16)utC#_41Vk zHyear8tdLA6N5{ts<}J{T6U0td2*jdY?mttFZ8!srM1izOxivF4M_s@w*msxyOjSU zV5Dc;k-Xl>YSP7d(d^!db##CD#JO7k_dMSO)kZsn95$h`YS6VKJfh4hQ82Pqas-GD zJF7oc>^wd8i$Q|gs*JFzMoX=oQzJ17R*3>GDT{@oO3CIi@s4bbg_OAzOI)o=Hh!oa zlE19=3#?tMdjUtIAW+hh7c6Dps>=#_=!&mwtR$vr-kCwuQpf0b?J}N;MsZ7-wJ$*# zr;~C88LowD+k#QnLZY+iLTJgdm-9?fDu#0E+vVL&-B zZXmz}A!6Xz)OP-jA@kVs*&>aC&CsvbUd3`BQ5pydb;oYirq;y7qgRdNYUJ0_lV;dk zq_kE@M|1R{FLMRqnAun%i{##xKOkwszdH+OA*eB zb+(3GSDO{8>Zcs(aAKIG0;%ouPY)ww2{*>s9v2P%LtOj52|&MD$y5k9cI&@j6`d~2 zt;v2KoO!P^rIv1;GV~IC*~`FzfKmJqXBURs<7`c-KxBu(f`)T_;Bv;whjB8g6XLq8X%ePuQ}^fWa%s(h13$ycrDw6*Dh!Yt}@G!&j>r z#ertwKs=O!>rgw4h9M2!T$&%DnePHerGwZJDWR!p7JHOFRX zU(cJ_IOqaoj=Q=`ZHXkxGIW68tA)9jkFtdR;Ox0SXi+lX&H@g}zs`7sDo?E}vZp#^ z4BeF}!bm26$zb&?OHyu#9)ef`lNTGcWJy@mEMBbvAO^d*HmBQJL_|(*BBah@JUNwJ zt2|*mv3LiQHix*PPAb2Z)hTF}lzxp`kaOU;IFXQX9d#PBVnQ8q_Y43t>MWwfNyQlS zV8W|Ka*HmtTG4yu)WMhCNu5q%MqMc;$DzAJXb@!EuE_+P8rvt!B4KzyY=jN7LREc1 zDykB;cOd2b>#7PB$i1(&i8yf$sSIo0A`Ih}!I(-!A#1~O#pE| zYjoHJnsf@ZlDG~^dEwM(GxR<*uLW?19M8?~#}f*?!_HWa$P>*8xRJL<7Ls~iYCMy& zpF8G5G9o3!r7QWzEG>ce9LidYTFA)gn#L`#kDgRAO?z(z4936HaOaol(!6V9kMdQu zoT`juGkg?Jd~Pe4)q7$CbjE8rfN|EKLsj=jiK0TUF}hz0R4Ja1Ok-}AYibBmE(>kG z!zaDis>g~RSO!?3iRfEQ{pd1wPI@8L^|ybi?Ia6PX7searyxOhNqPQA`}a{=NL6Cg z2jQv~*zl`>myq1&G_1mUWF6kd&(LV`{))h(56MgLh9t~ zwo!blm^97KqJ1g=HX3#e+$EwFI5xZy#f_)fk+2qPF^i}Yw#6c8DoDVn#9tZ=n_D&MiQ7k| z3P3so(rvv?mM>!L#CBHxG4p{lovpQ4=oG@lg5!8sbPmIMt*Tpisz!9Qx+n<8K$yc4 z!bAZ#M_Ijrpg&xeGb-+rrHr_(15{Z=F_^4E5lA$(5)6DeImteLa1n4|rEHU?lH8hr zGf}_JVTXX3yYs#nM}_DG@oh7k@ONTaNsnqRimQ3=S$Ik7;iy<9w7__4=zBW9YZs1n7pzfEFbx?F${B|)^4Qrn|i7`sF3$Sb1U`6%`jI!!`5 zPQAs#_wnUA5y1k>=({xmL_;QQd6E!Ti zq*ykZIS9ZmA8Bk3l3lt8y)cF4*+l2V9AeS_qErnk3F{jD$*6uZHM!!qPgzm*$qBI} zU&wecdZd&&zCJ6*ur9&3PEkRQ<*hI4TgDP}moACeva1~wBG+I=ciK;HO!KuwlFT?R z43%P&%FBq<#0cnP!hSIN%=y{Ld0Db?>LGhRKzHM>E}S7!(znF-7nnOtu!sU{${g7J z4)%WW%m5=exo7Fu?W9(o_;ET^5gVpUXQKfNSWkqVpR(nw6EPYJWFnM0Uv-{t6;VT+AuI9!fi~PLoiW(2e%W<6 z*WN;r!MAm65yjY&%x-3Fas2EaFCwZTIx(?C8{Cne6l}F1gjGq?Cn!F#NbTiE&B$JB z(4`}OC0Tjgb@d1ygPxF|7O?w{4=tJ((KMQGVzmK3r0chRY^ZCQzmO=7!m<#@W2xRz z$2_tOZ?P9547h(0^-Y~i5K8c*MK-KA)l)TU9et+GWAZwdu?P2NZcZN~dJJ~LJvmiu zmtst9ovI`m(h^aoFosmCg=nPI1UYbj(u7s2JzC7syIM6Js1LO-mk0cb-}_bfKGb zWhqZP%%s_oURu(a5m6WpZY}oAC>jWxr~yH%LSyHuN&D#9$Lux?B=7BZ*F_EHc&~AV zajs8DK{GMZpd?Jo0k9@@K{u*=(QH1(4sGfJt0OMYn^2iHytG?DK5rD#iv-`ZM>jeT z1jRfksm0PGFcB1AnFC(5Ra5NJb<}y2R(ExB>Q8dI0|jGIsQd7&-lq=4Aw~AYFpQivM>bvfkP`!=@oOYNSd#RAU8&H#ws#jYGr1)crwLH zYJ|FTCgmo6Ai&?3A$=6=@3sLn{)(QQ+qa^^l}k3K98UQx8M8(?aU8Z1NE4b2#E?CZ zim^>crKBtodw?D)9_O?|JimjVZCZ{eR9htTa7x7G6E=y0`j^y@^=$ftlPJaw30y|( zpCSmRgkF`fg$24f_CcnOZNU&U8Va}3iBPtqgvsN(0~m{D35LP`z}P2c7MpPE+yw!I zv@!~L{#mo#Lu+c8Rd4bbD?_F=vTD~<)R7UN3Vh=W!m8xZPAyhN(}dMwObo68*rte& z0$hb_ZacvnHI{x-G?hGIG=o&oSQy3*J(`$|)6g;T&C#VURJPPP_6M5bmC z9$Af<3T&1gUoAxa!l8iY@96-1>iAbLMFu?W>hIEhVU@P zq?S{B+0xBd>%43pK|Z0CaC)ABL6f1@ks{<)jn*;%YM3dLZaVt4i5*6xrEc@Oa4S$E zRnomAPAY8we}Ec(#2Ur!&(4tN44=NRqb^fgT%MRPmM~}1+vftJ$y6Bp48Gt5-rV-x z1a_0fSkr)xLEVJ%fl&-G>2jM0~O`HH+HH%hH}*Tv^tkh` z@CtJ|q~INq?QKLxp)p8#*@dH9J9HB252Crimnib#0}~KBCD?rAW%2sdHI@>_hSsN9 zM5;8=_|wBX6!TSS))Y&YN$>Gov_xEejxaTBC^&8Cj2@uVfo}u_D5F~%$2UqBi4t+# zbb$>_;tn1|9?S?WH7BD@6T4hoB|2q8o_OYwX7H^ zb^Mf|GVN%lKqPVv&jo5w=&^+F_sQH3QdHGLOwcc5;U(cs#SRh&Rvq6yk|^t(mc{n@ znaUPu04={A+gR^eMY&6HNvso;iUJMP#n3LSP(|IkS6w?C@ffy9Ub%1>>{nKiR~)6o zTAn!fKI9~q6L;3>WKvwW1oOf#k)^x?wY_jQ7A4Af7dC>Fr(8M9XkfKf_5h|HbDIxI zbq=#Wss|_)x)#wgVZ2R*JHlc)g=>1z$^@Sgv^-D{H^3QW7nL&4qCfOrn&obUJfseJ z1hU&^1c$STLgiv7FH@!3x3V?_Eg_E|s=T&;T;85Ep)JxdoMPStFzE_SudR~>?ekKf zY^>q1bfAJ)}cl;AGYFFlDZLfWbtejaH9XFtT4#Fk=#CIo-5BBh^* zpN1EP=K}G6OlXM}Xlkfu##fDpEUVJXDUDQmjI&W@L?1ni!7rAx!XwL6(D;P>B6g%M z*iDu@wVYX+Kp>7;)> zWVVZv#vV9M{aC{8jFSP2H9s3}mMm~2eV#u^O%jLPf^Xo5-_F1@$cHT;udDCT0sG*M zw}DYcpB!sP{an9iwT~I!?X;T6^t^>`y3Q?I6}k>w-QgYvp0^28iy072oiCT z&t|k`fnIdx0Aj45*K&nY% z4LNU$Hccevv|5yvZHU}z;Kk<15{>M*d!5?5n@3yCNX0v`#%e+718fjPJQn?_x@wLf z7=uf$MDRm5ui)YO9@{6DoKEFg!eM@lS)+`&dJ%nln=Vs2gfq2Hh=-7)@>~>S5SO5Z-U%|)TCX}F2g!ryyJaFCM0&&rnVhwx1ug= z4Sqdii7=zY*wmoJK0Tva7$(EGh&fk zZZj~V?SIKZL*Iz~$&T4u$VYi9(930KnNF~31FkOQ04+r|6b{-~{al(A%_Z`j2i3mX z1k^N&?8Rki|9>M~T1z`;QztV+A-(b zE7NiCcP^Prd7LmXn5{3T5kZe5Q7^f_2wNO#k5818YL>J*Ba~2Yqe$93)O&AsO*rOT zyAC>GfQ(|0y}z=KKYH}@y^XwA_rSpaWAHAvZ}IN?{-ytzYpHg~|8e0(FZm4ki66%| zV~Fyh@M-XXCjOY_r4+w^wfYV4Zuseyi!(+SeJcHqfA-b+Wq~GA_+b}zyZUZ(gZ?*6 zzy5LYTjg)k+sX(F)Mp=cX6_O`p=2W3QtOtyi?5DvysWENj^Xbvn|b4S5ML~Oc8~b3 zP~Y0ulrAnAy_|Bt??f{{5D&$ecKG^R)>Zr529__w+h@)Al9FgR0auR%#GX|ZY3(@e zFWw&i9Q8x)@2w8E8-3%qT-&d6`#86xquVY4tKgnNy@!8+WzFz#$aiWx(@I{5m10F*G zD0=BHa}mD5U(~mJsX>hoUVnPF!}uu2cN*l;AJZaQpgTE=lrqiwwz-d-GkCv0_NQK_ z%5HM&Y4VaLHZr_#x^6zcb8~$^K4;|lKR*^;&1*=Yo;R$J&t;#!W?oxzp8aON<pDqdKv1`4t!t7 zl-w5J8i z?K#IYEX6Tj!$jR0*J{S5+;zW4b)Ulxz9fn?{o8x)aPvF&NO>5}E-=keE6D-t2?i+O{Psr1-Cq0FiOwz;1?DaGjFYF8}AH*{VJzfXD z>JOSVToAE_atwQF69OYLK) z5mrTU0E}GG80NZaDG7-2KBOGAOa#oxVw9NU&~#W3Qyo*xORgm}@e1#`r)a+;EUCu$ zj!?u?ze52kpdHRu$sQ|WG$58nosjt8Hmo(8j^ZX>MRR*To-YRQ`Dc6ynOAq#F zOR!NrCS}D}da3P)hf^Vq%`zB1#QTHS8YEv2gnwVO!XB^JA*Whd(ytp79tZ4n*mySV z+ICu@b+E($B}&jKEAhvObt~-QpUAb?3p59L z7xRBpx~F!L4AX}_6|ifp)Sm?QgiH(!_Qs-9^bGJNUdz$ysZwU~oA~o+ zI!MdqSoAR;ZQ5CRX>zl?B?{S(YWD2cWp&vY3XPIxjdTCY?y=uLj zS@gBqki!D`v^i02Z;J}&2S=1)0=;_1b=g@D(B~8YL2C>=LhlGj7>E#aynh)~pH-AJ z8IpxFI(;ORz`!e{q((%EHGOuyrBrNR7ZB9>)F3FatETK&!3cSe1v$?_p)O-wH}O;F zyXw*0#Id!>XjXGkkvm7!z6rZ*cqte-;gH9<#~iR1fi(Np_A_7ooTsrO1?UB1!L0^r z8%7J)p4@X-A)8Bqs(ia8G)*!({p&}uQ$^L}5b1Plp1P#$L)wa{fPZy6p|Zu-sD8{o zFq|*3+EB`du0JhXD5U=tZuo+GS!Ep1bC|-0yD{9R*Xt2NwZno|MnTXJ8!d@L6gM&R zbp#47_FWD`+dxntR#k0kkoCprAQBm^U~5LhqFf3kkPP#Vy=?Q7Y6|bSLo&kpQJAkN zQA)m@GBQU2z=rW4yK@kqByLebqy5*U7`#rCd4p_&c>@}6VjE$n+yQpMcAKFcPK58b ze<`tHuuhD)47O>sA*lK}Tka?T)WfxDk}#LaK#H0r+L_HKigHM-2l%Yw$}jP;wBn6m zJ-~zEIk{GAMF%BkyOdnA47%>30;=UkQYEW<JiaBj18{# z$C+M@zpHS|X%{B~+O8i5keZxxo#9kwcLqg6mr! zm*<6Bp<4!Qn=o1^ifG5Gts+QGIU65XX$(AFZ+oOp`z+|K7BxFmwDC4%lF-D5gH{M@Y}-(1k5pGQ{-(EQz&7_ql>Zy|ING+# z%&hA01g;tvegEoqGRvG>I8kk;joJ7L=6-512ieP;`MK(nI_4zdLk6Y1HTt1ZRBUm@ z$SomcZ2K6EY4E2E!9k5=@?3EQW^)4}f1>G`ct5C_>pCiqPSFYSYCR_f0hYs@j@!Ju zc#e|q7ZLVwh>f}&tT(gUCGDBNvYA5!c4hu5@Xq8Cv$24Gr_@KuRR==+m7E!1>rfMS zLl1;DSS}PxEpsh%i_boK-7mTQUaDd<`a1Cb6pk+UHKvgR_q2EDs`Kfz>i_Bt7$ z6%F4&$3Q0{fsDZ}+RU?^K>ZsAXj2Q)?tq|J*^I8yfq!&KNDH4}JELoWKc^H_rU`i# zcNX{GBk}rvEl{7@?lT&%{pr{PceV{&VcyGqjh8g{xk; zaLUFSS+Qv^gx9Ch+awh4m`e6rZcPz%)B60dN^|^56+eTnHr5$wjdjG;Uq{m!S(-U% zggsvoms*Q!4cpr})?cib&)opkbbnq`r38Pnlq}&`E%6HS_tEl2DyH;$+AMcOc!&x4 z$^dCLtxIsA)b3YNX2s%1H4L_aZvt&uO7a$!B0dZf_BKl+!-psYpJ(p$Rw|Py5rF@e z`;OMa8k<`BB4H~Au@0qWVcsXlUC ztK9{84ZmF>z<2zhCd5a44Oin(i;p`M;wy0C0h=Po>z;!FT7TO|umH>Fw`K_{#Vfru zjlQOgjTgil@_Kp5TWz~S?y*Rhbe%4qC1n=H!(!mt zqwP$S-$PdwNC~B!yu)`F6+aKJ-H1EPpp8QL*(GQYJ1!4&e2UU3wlMhg9Z+8+>s`k_ zu!S}PIrq|iZJT5C1NAd@%ku7W23(tL54JcqhTLU0O06_jan2&-d1fXA;npc zU<3c=UNhjYhu`AIf2dJ;!6e|IN{gegbs@5`wC|=m%Z6u}8>Gxo7dkUy1_;n3m$DY9 zY9H5I_0(Xg^(^7P^M`=~3d8|-xh_#kzWymhjRq|I7T^P@_+w(qRY%(7}Mvp=6r_C5JLkwh>PCqu)O> zr_2F4fE(Ul*#~JFNY3_T5x}CKQ?-VCH?aBF_%;kaN6DrLbT3pd)OeBu8np#?$!|Vk zBpM95M%;DVpQR@m-|E7);=~rRJAY0rEblYuQbMm3nZ^%#v+PFYx$NP!yUMu2MM#ydb>3EE4`Lr`3emBOg z+D8a%P|L;KTht?O;kzI`rN#A|qwX*LOg|h_QaI=tp^SWgAAg_ooiUqLbNd1+gB>jc z{e@cDcE^ku-3^HhOE;>12$&`-Xgg}EjFeAe4rD7!WikA4z%{p?7PPP}M+pas;Qj$z zBvvl5KXDR8%>BVu+HctoIE!fBDI^gi^SW+4s?R7% zB#jrs9ef!HFb`xKnTRX)E9|Y4uxjd;f-T*k>57F(=ec8Wl7DA!3a*kCZbrF;l)4TP zBexmLJj4@pKI8IR?dCu>2T=<0G++2mgxSg&>tg_ zFdcxgP*h2tN{Z|_8REGQLXmbBa2D{ppvKZUQu^rSXz$MmhjQ1Jc3ZA&cC@%E@B8!J zZR)~RbScywykWtgusuru`{BfvmD8L>s|k);SCU= z;12|eSm)&22f1I0z9iuS&Vyy5sDz;9rGyM=pke|-`Rg+Wt|BPv6)05oI9EHn6Nvu+ zMk$I=-U##JN|02(D}so}A^Dz7vUxKkzb2`O0_h=Jb4~zPSa&XBtQmnkfxPegVSLF{ zhFEV-wc(5u1fT#ffWlu%0DryGXY4X&vF!E(2xrdNNjx~wu~V)=@idACIDXdw$x+wf zY^KehK)r4=qT~xx;o4X+3N~9OgwKV*$7$Ch$1%NFPl{CE7uo1o| zHE@9Uf0l)BfKOgGlbE&c%ziIaYHUlaC&NLeY6P|Q;8LXuxS-#jRx&1r`&GH`w{O!6d_qX`V#(&z+e}**OK(aUdo^ zv4)~l+;^3nwZf0V1F*iPUJ-zVl=+*RXxVi~!|2iIs|c`644{OkvUjRL={D{#vMr@i{% zLLo!war>_6?+{!RY<+`T7KLNFBwZwIX;nrfDtI&oJH|);W{R+zx2tkWq=!=5wElyN zbvTw*Y)or&Q|cne%sLudyu#-VcIOYTVC)*)bti%36;I4(GX49DCmBU@hMpq1ycBl6 z?}PMK;S}i*9x|>9T_CvK>2S{r%1-cjkm@KNKkQsPb$g4Yt>Qt*$lnNLw?utWB+QA52EBSN^pE&WSiN=@0Q8Aq zeUfPLpP{gLhg+4(CW!4>@66IRBuU!|O)!91HR-1vl!QFiNIBLjCj|pwFss+@6w|Cz zMj^j8G`!HJZG~wxI9lxI)c-^W1M30N+-`u^5_~KJK;!lHzXECiXfUAASiBbnn&>BD zTbrqT>3qK)8_F*Y`22l1yc)$dTbfV=imhuV*Tk}2b5hD!i!UJfB7{SNvx8K--}~6x zsNDa67|*l9N!e?3Ns-~R}aA{FdEy$G9H*}>!E&365kJ$2$siajgkIJ^LlP^?)! z!p3~MrKWgAojt~`4*BuSHniDeF9rx6nEND$<$=8DJt{le!)0JphYfPyxfK?!p(44D z``fvSq+e&lvjaP_#XZ0r4uYS;eYxiEh#3!984pW7b}dHw~myJFO%s7S(T5`tZRC1+)5MjLpWE%-s=j
  • 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 WEpz|?0Q>zA2LS-+1^K>Z0scQDo1hQ? literal 0 HcmV?d00001 diff --git a/src/test/resources/generatedXml/AAI-Grouping Service for Test-service-1.0.xml b/src/test/resources/generatedXml/AAI-Grouping Service for Test-service-1.0.xml new file mode 100644 index 0000000..2bafb2f --- /dev/null +++ b/src/test/resources/generatedXml/AAI-Grouping Service for Test-service-1.0.xml @@ -0,0 +1,69 @@ + + service-invariant-uuid + service + + + service-uuid + Grouping Service for Test + 1.0 + xxx + + + T + unbounded + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + instance-group-0-version-id + + + model.model-invariant-id + instance-group-0-invariant-id + + + + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + instance-group-1-version-id + + + model.model-invariant-id + instance-group-1-invariant-id + + + + + + + + model-ver + + model-ver.model-version-id + service-instance-version-id + + + model.model-invariant-id + service-instance-invariant-id + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml b/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml new file mode 100644 index 0000000..b246667 --- /dev/null +++ b/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml @@ -0,0 +1,32 @@ + + instance-group-0-invariant-id + resource + + + instance-group-0-version-id + groupingservicefortest..ResourceInstanceGroup..0 + 1 + DDD0 + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + instance-group-version-id + + + model.model-invariant-id + instance-group-invariant-id + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml b/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml new file mode 100644 index 0000000..d49e480 --- /dev/null +++ b/src/test/resources/generatedXml/AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml @@ -0,0 +1,32 @@ + + instance-group-1-invariant-id + resource + + + instance-group-1-version-id + groupingservicefortest..ResourceInstanceGroup..1 + 1 + DDD1 + + + T + unbounded + + + + model-ver + + model-ver.model-version-id + instance-group-version-id + + + model.model-invariant-id + instance-group-invariant-id + + + + + + + + -- 2.16.6 From 35c2e7409230646c6bd876f747e68dd00c368a1e Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Fri, 30 Nov 2018 10:52:50 +0000 Subject: [PATCH 07/16] Upgrade to sdc-tosca version 1.4.7 Change-Id: I186683ff734747e64a561391ec4314bc716a0cd3 Issue-ID: AAI-1983 Signed-off-by: mark.j.leonard --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 69810ce..f8f04c4 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 1.2.2 - 1.4.6 + 1.4.7 1.18 2.1 0.13.2 -- 2.16.6 From 0c9acb0e4e7ecde50c274ba60535fafc968f59ba Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Wed, 5 Dec 2018 18:06:02 +0000 Subject: [PATCH 08/16] Compare YML files as Strings, not XML Simplify the JUnit testing for the YAML file extractor and compare the actual and expected files using Strings (with line endings normalized). Change-Id: Ie78d3506511b4a1d5333c1b0b95a676bbc2ab5e2 Issue-ID: AAI-1991 Signed-off-by: mark.j.leonard --- .../org/onap/aai/babel/util/ArtifactTestUtils.java | 51 +++++++++------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java index cf96d24..3cbe194 100644 --- a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java +++ b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java @@ -20,7 +20,10 @@ */ package org.onap.aai.babel.util; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import java.io.IOException; @@ -30,9 +33,9 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; +import java.util.HashMap; import java.util.List; -import java.util.Set; -import java.util.function.Function; +import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.custommonkey.xmlunit.Diff; @@ -48,19 +51,22 @@ public class ArtifactTestUtils { private static final String JSON_RESPONSES_FOLDER = "response/"; private static final String CSAR_INPUTS_FOLDER = "compressedArtifacts/"; - public void performYmlAsserts(List toscaFiles, List ymlPayloadsToLoad) { - assertThat("An unexpected number of YAML files have been extracted", toscaFiles.size(), - is(ymlPayloadsToLoad.size())); - - Function loadResource = s -> { - try { - return loadResourceAsString(s); - } catch (IOException e) { - throw new RuntimeException(e); - } - }; - Set ymlPayloads = ymlPayloadsToLoad.stream().map(loadResource).collect(Collectors.toSet()); - compareXmlPayloads(toscaFiles, ymlPayloads); + public void performYmlAsserts(List toscaFiles, List ymlPayloadsToLoad) throws IOException { + assertThat("An incorrect number of YAML files have been extracted", toscaFiles.size(), + is(equalTo(ymlPayloadsToLoad.size()))); + + Map ymlMap = new HashMap<>(); + for (String filename : ymlPayloadsToLoad) { + ymlMap.put(filename, loadResourceAsString(filename)); + } + + for (Artifact artifact : toscaFiles) { + String fileName = artifact.getName().replaceFirst("Definitions/", "ymlFiles/"); + String expectedYaml = ymlMap.get(fileName); + assertThat("Missing expected content for " + fileName, expectedYaml, is(not(nullValue()))); + assertThat("The content of " + fileName + " must match the expected content", + convertToString(artifact.getPayload()).replaceAll("\\r\\n?", "\n"), is(equalTo(expectedYaml))); + } } /** @@ -114,21 +120,6 @@ public class ArtifactTestUtils { return ArtifactTestUtils.class.getClassLoader().getResource(resourceName); } - private void compareXmlPayloads(List toscaFiles, Set ymlPayloads) { - for (Artifact artifact : toscaFiles) { - boolean payloadFound = false; - for (String ymlPayload : ymlPayloads) { - - if (compareXmlStrings(convertToString(artifact.getPayload()), ymlPayload)) { - payloadFound = true; - break; - } - } - assertThat("The content of each YAML file must match the actual content of the file extracted (" - + artifact.getName() + ")", payloadFound, is(true)); - } - } - private String convertToString(byte[] byteArray) { return new String(Base64.getDecoder().decode(byteArray), Charset.defaultCharset()); } -- 2.16.6 From 0408ea82dc1a55b4f30e7062eb91e76b94091392 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Wed, 5 Dec 2018 18:29:26 +0000 Subject: [PATCH 09/16] Throw an Exception for XML test file mismatches Fix a bug that masked differences between expected and actual XML files. This affects JUnit tests only. Change-Id: I72c779c008baa9fba52c09a0751cdd25bdc0e5cc Issue-ID: AAI-1991 Signed-off-by: mark.j.leonard --- .../aai/babel/service/CsarToXmlConverterTest.java | 377 +++++++++++---------- .../org/onap/aai/babel/util/ArtifactTestUtils.java | 32 +- 2 files changed, 211 insertions(+), 198 deletions(-) diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 84a2934..1d39f6d 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -47,196 +47,201 @@ import org.onap.aai.babel.service.data.BabelArtifact; import org.onap.aai.babel.testdata.CsarTest; import org.onap.aai.babel.util.ArtifactTestUtils; import org.onap.aai.babel.xml.generator.XmlArtifactGenerationException; +import org.xml.sax.SAXException; /** * Tests {@link CsarToXmlConverter}. */ public class CsarToXmlConverterTest { - private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; - private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; - - private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; - private static final String SERVICE_VERSION = "1.0"; - - static { - if (System.getProperty("APP_HOME") == null) { - System.setProperty("APP_HOME", "."); - } - } - - // The class to be tested. - private CsarToXmlConverter converter; - - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Before - public void setup() { - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); - - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); - - converter = new CsarToXmlConverter(); - } - - @After - public void tearDown() { - converter = null; - } - - @Test(expected = NullPointerException.class) - public void testNullArtifactSupplied() throws CsarConverterException { - converter.generateXmlFromCsar(null, null, null); - } - - @Test(expected = NullPointerException.class) - public void testMissingName() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), null, null); - } - - @Test(expected = NullPointerException.class) - public void testMissingVersion() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), INCORRECT_CSAR_NAME, null); - } - - @Test(expected = CsarConverterException.class) - public void testNoPayloadExists() throws CsarConverterException { - converter.generateXmlFromCsar(new byte[0], INCORRECT_CSAR_NAME, SERVICE_VERSION); - } - - @Test(expected = CsarConverterException.class) - public void testCsarFileHasNoYmlFiles() throws CsarConverterException, IOException { - converter.generateXmlFromCsar(CsarTest.NO_YAML_FILES.getContent(), CsarTest.NO_YAML_FILES.getName(), - SERVICE_VERSION); - } - - /** - * Test that an Exception is thrown when the Artifact Generator properties are not present. - * - * @throws CsarConverterException - * if there is an error either extracting the YAML files or generating XML artifacts - * @throws IOException - * if an I/O exception occurs loading the test CSAR file - * @throws IOException - * @throws XmlArtifactGenerationException - * @throws CsarConverterException - */ - @Test - public void testArtifactGeneratorConfigMissing() throws CsarConverterException, IOException { - exception.expect(CsarConverterException.class); - exception.expectMessage("Cannot generate artifacts. System property artifactgenerator.config not configured"); - - // Unset the required system property - System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), - SERVICE_VERSION); - } - - /** - * Test that an Exception is thrown when the Artifact Generator's Group Filter properties are not present. - * - * @throws IOException - * @throws XmlArtifactGenerationException - * @throws CsarConverterException - */ - @Test - public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() - throws IOException, XmlArtifactGenerationException, CsarConverterException { - exception.expect(CsarConverterException.class); - exception.expectMessage("Cannot generate artifacts. System property groupfilter.config not configured"); - - // Unset the required system property - System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE); - converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), - SERVICE_VERSION); - } - - @Test - public void testServiceMetadataMissing() - throws IOException, XmlArtifactGenerationException, CsarConverterException { - converter.generateXmlFromCsar(CsarTest.MISSING_METADATA_CSAR.getContent(), - CsarTest.MISSING_METADATA_CSAR.getName(), SERVICE_VERSION); - } - - @Test - public void generateXmlFromSdWanCsar() throws IOException, CsarConverterException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-SD-WAN-Service-Test-service-1.0.xml"); - filesToLoad.add("AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml"); - filesToLoad.add("AAI-Tunnel_XConnTest-resource-2.0.xml"); - filesToLoad.add("AAI-SD-WAN-Test-VSP-resource-1.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SD_WAN_CSAR_FILE); - } - - @Test - public void generateXmlFromNetworkCollectionCsar() throws IOException, CsarConverterException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-TEST SVC_1-service-1.0.xml"); - filesToLoad.add("AAI-TEST CR_1-resource-7.0.xml"); - filesToLoad.add("AAI-testcr_1..NetworkCollection..0-resource-1.xml"); - filesToLoad.add("AAI-ExtVL-resource-40.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), - CsarTest.NETWORK_COLLECTION_CSAR_FILE); - } - - @Test - public void generatePortMirrorConfigurationModel() - throws CsarConverterException, IOException, XmlArtifactGenerationException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-Port Mirror_Test-service-1.0.xml"); - filesToLoad.add("AAI-Port Mirroring Configuration-resource-35.0.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.PORT_MIRROR_CSAR); - } - - @Test - public void generateXmlFromServiceProxyCsar() - throws CsarConverterException, IOException, XmlArtifactGenerationException { - List filesToLoad = new ArrayList<>(); - filesToLoad.add("AAI-Grouping Service for Test-service-1.0.xml"); - filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml"); - filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml"); - assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SERVICE_PROXY_CSAR_FILE); - } - - public Matcher matches(final String expected) { - return new BaseMatcher() { - protected String theExpected = expected; - - @Override - public boolean matches(Object item) { - return new ArtifactTestUtils().compareXmlStrings((String) item, theExpected); - } - - @Override - public void describeTo(Description description) { - description.appendText(theExpected.toString()); - } - }; - } - - private Map createExpectedXmlFiles(List filesToLoad) throws IOException { - Map xmlMap = new HashMap<>(); - for (String filename : filesToLoad) { - xmlMap.put(filename, new ArtifactTestUtils().loadResourceAsString("generatedXml/" + filename)); - } - return xmlMap; - } - - private void assertThatGeneratedFilesMatchExpected(Map expectedXmlFiles, CsarTest csarFile) - throws CsarConverterException, IOException { - List generatedArtifacts = converter.generateXmlFromCsar(csarFile.getContent(), - csarFile.getName(), SERVICE_VERSION); - assertThat("Incorrect number of files generated", // - generatedArtifacts.size(), is(equalTo(expectedXmlFiles.size()))); - for (BabelArtifact generated : generatedArtifacts) { - String fileName = generated.getName(); - String expectedXml = expectedXmlFiles.get(fileName); - assertThat("Missing expected content for " + generated.getName(), expectedXml, is(not(nullValue()))); - assertThat("The content of " + generated.getName() + " must match the expected content", - generated.getPayload(), matches(expectedXml)); - } - } + private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; + private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; + + private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; + private static final String SERVICE_VERSION = "1.0"; + + static { + if (System.getProperty("APP_HOME") == null) { + System.setProperty("APP_HOME", "."); + } + } + + // The class to be tested. + private CsarToXmlConverter converter; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Before + public void setup() { + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, + new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); + + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, + new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); + + converter = new CsarToXmlConverter(); + } + + @After + public void tearDown() { + converter = null; + } + + @Test(expected = NullPointerException.class) + public void testNullArtifactSupplied() throws CsarConverterException { + converter.generateXmlFromCsar(null, null, null); + } + + @Test(expected = NullPointerException.class) + public void testMissingName() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), null, null); + } + + @Test(expected = NullPointerException.class) + public void testMissingVersion() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), INCORRECT_CSAR_NAME, null); + } + + @Test(expected = CsarConverterException.class) + public void testNoPayloadExists() throws CsarConverterException { + converter.generateXmlFromCsar(new byte[0], INCORRECT_CSAR_NAME, SERVICE_VERSION); + } + + @Test(expected = CsarConverterException.class) + public void testCsarFileHasNoYmlFiles() throws CsarConverterException, IOException { + converter.generateXmlFromCsar(CsarTest.NO_YAML_FILES.getContent(), CsarTest.NO_YAML_FILES.getName(), + SERVICE_VERSION); + } + + /** + * Test that an Exception is thrown when the Artifact Generator properties are not present. + * + * @throws CsarConverterException + * if there is an error either extracting the YAML files or generating XML artifacts + * @throws IOException + * if an I/O exception occurs loading the test CSAR file + * @throws IOException + * @throws XmlArtifactGenerationException + * @throws CsarConverterException + */ + @Test + public void testArtifactGeneratorConfigMissing() throws CsarConverterException, IOException { + exception.expect(CsarConverterException.class); + exception.expectMessage("Cannot generate artifacts. System property artifactgenerator.config not configured"); + + // Unset the required system property + System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE); + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), + SERVICE_VERSION); + } + + /** + * Test that an Exception is thrown when the Artifact Generator's Group Filter properties are not present. + * + * @throws IOException + * @throws XmlArtifactGenerationException + * @throws CsarConverterException + */ + @Test + public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() + throws IOException, XmlArtifactGenerationException, CsarConverterException { + exception.expect(CsarConverterException.class); + exception.expectMessage("Cannot generate artifacts. System property groupfilter.config not configured"); + + // Unset the required system property + System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE); + converter.generateXmlFromCsar(CsarTest.SD_WAN_CSAR_FILE.getContent(), CsarTest.SD_WAN_CSAR_FILE.getName(), + SERVICE_VERSION); + } + + @Test + public void testServiceMetadataMissing() + throws IOException, XmlArtifactGenerationException, CsarConverterException { + converter.generateXmlFromCsar(CsarTest.MISSING_METADATA_CSAR.getContent(), + CsarTest.MISSING_METADATA_CSAR.getName(), SERVICE_VERSION); + } + + @Test + public void generateXmlFromSdWanCsar() throws IOException, CsarConverterException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-SD-WAN-Service-Test-service-1.0.xml"); + filesToLoad.add("AAI-SdWanTestVsp..DUMMY..module-0-resource-2.xml"); + filesToLoad.add("AAI-Tunnel_XConnTest-resource-2.0.xml"); + filesToLoad.add("AAI-SD-WAN-Test-VSP-resource-1.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SD_WAN_CSAR_FILE); + } + + @Test + public void generateXmlFromNetworkCollectionCsar() throws IOException, CsarConverterException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-TEST SVC_1-service-1.0.xml"); + filesToLoad.add("AAI-TEST CR_1-resource-7.0.xml"); + filesToLoad.add("AAI-testcr_1..NetworkCollection..0-resource-1.xml"); + filesToLoad.add("AAI-ExtVL-resource-40.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), + CsarTest.NETWORK_COLLECTION_CSAR_FILE); + } + + @Test + public void generatePortMirrorConfigurationModel() + throws CsarConverterException, IOException, XmlArtifactGenerationException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-Port Mirror_Test-service-1.0.xml"); + filesToLoad.add("AAI-Port Mirroring Configuration-resource-35.0.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.PORT_MIRROR_CSAR); + } + + @Test + public void generateXmlFromServiceProxyCsar() + throws CsarConverterException, IOException, XmlArtifactGenerationException { + List filesToLoad = new ArrayList<>(); + filesToLoad.add("AAI-Grouping Service for Test-service-1.0.xml"); + filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..0-resource-1.xml"); + filesToLoad.add("AAI-groupingservicefortest..ResourceInstanceGroup..1-resource-1.xml"); + assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SERVICE_PROXY_CSAR_FILE); + } + + public Matcher matches(final String expected) { + return new BaseMatcher() { + protected String theExpected = expected; + + @Override + public boolean matches(Object item) { + try { + return new ArtifactTestUtils().compareXmlStrings((String) item, theExpected); + } catch (SAXException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void describeTo(Description description) { + description.appendText(theExpected.toString()); + } + }; + } + + private Map createExpectedXmlFiles(List filesToLoad) throws IOException { + Map xmlMap = new HashMap<>(); + for (String filename : filesToLoad) { + xmlMap.put(filename, new ArtifactTestUtils().loadResourceAsString("generatedXml/" + filename)); + } + return xmlMap; + } + + private void assertThatGeneratedFilesMatchExpected(Map expectedXmlFiles, CsarTest csarFile) + throws CsarConverterException, IOException { + List generatedArtifacts = + converter.generateXmlFromCsar(csarFile.getContent(), csarFile.getName(), SERVICE_VERSION); + assertThat("Incorrect number of files generated", // + generatedArtifacts.size(), is(equalTo(expectedXmlFiles.size()))); + for (BabelArtifact generated : generatedArtifacts) { + String fileName = generated.getName(); + String expectedXml = expectedXmlFiles.get(fileName); + assertThat("Missing expected content for " + generated.getName(), expectedXml, is(not(nullValue()))); + assertThat("The content of " + generated.getName() + " must match the expected content", + generated.getPayload(), matches(expectedXml)); + } + } } diff --git a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java index 3cbe194..fa0b784 100644 --- a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java +++ b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java @@ -51,6 +51,16 @@ public class ArtifactTestUtils { private static final String JSON_RESPONSES_FOLDER = "response/"; private static final String CSAR_INPUTS_FOLDER = "compressedArtifacts/"; + /** + * Specific test method for the YAML Extractor test. + * + * @param toscaFiles + * files extracted by the YamlExtractor + * @param ymlPayloadsToLoad + * the expected YAML files + * @throws IOException + * if an I/O exception occurs + */ public void performYmlAsserts(List toscaFiles, List ymlPayloadsToLoad) throws IOException { assertThat("An incorrect number of YAML files have been extracted", toscaFiles.size(), is(equalTo(ymlPayloadsToLoad.size()))); @@ -70,22 +80,20 @@ public class ArtifactTestUtils { } /** - * Compare 2 XML strings to see if they have the same content + * Compare two XML strings to see if they have the same content. * * @param string1 + * XML content * @param string2 - * @return true if similar + * XML content + * @return true if XML content is similar + * @throws IOException + * if an I/O exception occurs + * @throws SAXException + * if the XML parsing fails */ - public boolean compareXmlStrings(String string1, String string2) { - boolean similar = false; - - try { - similar = new Diff(string1, string2).similar(); - } catch (SAXException | IOException e) { // NOSONAR - similar = true; - } - - return similar; + public boolean compareXmlStrings(String string1, String string2) throws SAXException, IOException { + return new Diff(string1, string2).similar(); } public byte[] getCompressedArtifact(String resourceName) throws IOException { -- 2.16.6 From 0babe59ef157c1d767bce05cf0bebc12d962d1e0 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Wed, 5 Dec 2018 18:59:58 +0000 Subject: [PATCH 10/16] Add null check for Group member with no A&AI Model Protect against a Null Pointer Exception when any member of an Instance Group cannot be mapped to an output XML Resource or Widget. Change-Id: Icf099686681b8dffb86e9f003bd92ace6e8679b1 Issue-ID: AAI-1991 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index cae0324..615ad1e 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -307,18 +307,28 @@ public class ArtifactGeneratorToscaParser { */ private List generateResourcesAndWidgets(final ArrayList memberNodes, final Resource groupModel) { + log.debug(String.format("Processing member nodes for Group %s (invariant UUID %s)", // + groupModel.getModelName(), groupModel.getModelId())); + List 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()); + final String metadataType = nodeTemplate.getMetaData().getValue("type"); + + log.debug(String.format("Get model for %s (metadata type %s)", nodeTypeName, metadataType)); + Model memberModel = Model.getModelFor(nodeTypeName, metadataType); - log.debug(String.format("Generating grouped %s (%s) from TOSCA type %s", - memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName)); + if (memberModel != null) { + memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); - addRelatedModel(groupModel, memberModel); - if (memberModel instanceof Resource) { - resources.add((Resource) memberModel); + 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; @@ -384,7 +394,7 @@ public class ArtifactGeneratorToscaParser { * @param resourceModel * parent Resource * @param metaData - * metadata for populating the Resource IDs + * for populating the Resource IDs * @param resourceNode * any Model (will be ignored if not a Resource) * @param nodeProperties -- 2.16.6 From 8ee3630c60e56ca66361d623c3c8d40e1e6494e3 Mon Sep 17 00:00:00 2001 From: jimmy Date: Mon, 17 Dec 2018 13:52:03 -0500 Subject: [PATCH 11/16] Update to springboot 1.5.18 Change-Id: I69d083b8e1cf574b15dc20b26f2925004e22620a Issue-ID: AAI-2012 Signed-off-by: Jimmy Forsyth --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f8f04c4..b39d461 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ - 1.5.17.RELEASE + 1.5.18.RELEASE UTF-8 UTF-8 -- 2.16.6 From 83dd58fde9eb9ab0d1e6b69228b1695380d1a22c Mon Sep 17 00:00:00 2001 From: jimmy Date: Thu, 20 Dec 2018 14:47:24 -0500 Subject: [PATCH 12/16] Exclude tomcat from jersey starter Issue-ID: AAI-2032 Change-Id: I224d1b03f7187f7caac8220154fce3ee1eaffa1a Signed-off-by: jimmy --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index b39d461..3f6b7e3 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,12 @@ org.springframework.boot spring-boot-starter-jersey + + + org.springframework.boot + spring-boot-starter-tomcat + + org.onap.aai.logging-service -- 2.16.6 From 6833fb0a9a3f8c26688ad5c323eb266827b707c2 Mon Sep 17 00:00:00 2001 From: Serban Popescu Date: Wed, 23 Jan 2019 10:58:12 -0500 Subject: [PATCH 13/16] Do not try to unobfuscate clear text passwords The keystore password does not need to be decrypted if in clear text Change-Id: Ia0d8591e1d5ca6890fcb77295de9573921f6652f Issue-ID: AAI-2072 Signed-off-by: Serban Popescu --- src/main/java/org/onap/aai/babel/BabelApplication.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/onap/aai/babel/BabelApplication.java b/src/main/java/org/onap/aai/babel/BabelApplication.java index 868cff3..9cf1078 100644 --- a/src/main/java/org/onap/aai/babel/BabelApplication.java +++ b/src/main/java/org/onap/aai/babel/BabelApplication.java @@ -31,6 +31,8 @@ import org.springframework.context.annotation.ImportResource; @ImportResource("classpath:babel-beans.xml") public class BabelApplication extends SpringBootServletInitializer { + private static final String OBFS_PATTERN = "OBF:"; + /** * Spring Boot Initialization. * @@ -42,7 +44,8 @@ public class BabelApplication extends SpringBootServletInitializer { throw new IllegalArgumentException("Env property KEY_STORE_PASSWORD not set"); } HashMap props = new HashMap<>(); - props.put("server.ssl.key-store-password", Password.deobfuscate(keyStorePassword)); + String decryptedValue = keyStorePassword.startsWith(OBFS_PATTERN)? Password.deobfuscate(keyStorePassword) : keyStorePassword; + props.put("server.ssl.key-store-password", decryptedValue); new BabelApplication().configure(new SpringApplicationBuilder(BabelApplication.class).properties(props)) .run(args); } -- 2.16.6 From 7fcc74469c941c1834cd02b54ff5ca88a53bf83b Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Mon, 28 Jan 2019 11:42:54 +0000 Subject: [PATCH 14/16] Load type mappings from a group configuration file Change the filter-types.properties file: re-implement to use JSON content. Remove the System Property that defines the location of the file and add this to the Spring application properties. Initialise the type-to-model mappings using the key=value pairs from this JSON content. Refactor existing Junit tests to remove duplicated resource loading code. Change-Id: Idb2e962fe5cae39b70cc8cf16053d0a253f4fac0 Issue-ID: AAI-2121 Signed-off-by: mark.j.leonard --- pom.xml | 3 +- src/main/bin/start.sh | 5 +- .../babel/parser/ArtifactGeneratorToscaParser.java | 65 +++++++++------- .../xml/generator/api/AaiArtifactGenerator.java | 12 +-- .../xml/generator/data/GroupConfiguration.java | 45 +++++++++++ .../generator/data/WidgetConfigurationUtil.java | 46 ++++++++--- .../onap/aai/babel/xml/generator/model/Model.java | 39 +++------- .../onap/aai/babel/xml/generator/model/Widget.java | 7 +- src/main/resources/application.properties | 4 +- .../parser/TestArtifactGeneratorToscaParser.java | 9 +-- .../org/onap/aai/babel/parser/TestToscaParser.java | 31 ++------ .../aai/babel/service/CsarToXmlConverterTest.java | 21 ++--- .../service/TestGenerateArtifactsServiceImpl.java | 18 ++--- .../org/onap/aai/babel/util/ArtifactTestUtils.java | 51 +++++++++--- .../java/org/onap/aai/babel/util/Resources.java | 31 ++++++++ .../aai/babel/xml/generator/model/TestModel.java | 29 ++++--- .../babel/xml/generator/model/TestVfModule.java | 90 +++++++++++++--------- .../aai/babel/xml/generator/model/TestWidget.java | 19 ++--- src/test/resources/filter-types.properties | 1 - src/test/resources/tosca-mappings.json | 21 +++++ 20 files changed, 333 insertions(+), 214 deletions(-) create mode 100644 src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java create mode 100644 src/test/java/org/onap/aai/babel/util/Resources.java delete mode 100644 src/test/resources/filter-types.properties create mode 100644 src/test/resources/tosca-mappings.json diff --git a/pom.xml b/pom.xml index 3f6b7e3..0798d5c 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ rest-client ${aai.rest.client.version} - + org.springframework.boot @@ -176,7 +176,6 @@ -DCONFIG_HOME=./appconfig-local -DAPP_HOME=. -Dartifactgenerator.config=./appconfig-local/artifact-generator.properties - -Dgroupfilter.config=./appconfig-local/filter-types.properties -DKEY_STORE_PASSWORD=${KEY_STORE_PASSWORD} diff --git a/src/main/bin/start.sh b/src/main/bin/start.sh index c71acca..1ee1db6 100644 --- a/src/main/bin/start.sh +++ b/src/main/bin/start.sh @@ -3,8 +3,8 @@ # ============LICENSE_START======================================================= # org.onap.aai # ================================================================================ -# Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. -# Copyright © 2017-2018 European Software Marketing Ltd. +# Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. +# Copyright © 2017-2019 European Software Marketing Ltd. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ fi PROPS="-DAPP_HOME=${APP_HOME}" PROPS="${PROPS} -DCONFIG_HOME=${CONFIG_HOME}" PROPS="${PROPS} -Dartifactgenerator.config=${CONFIG_HOME}/artifact-generator.properties" -PROPS="${PROPS} -Dgroupfilter.config=${CONFIG_HOME}/filter-types.properties" PROPS="${PROPS} -DKEY_STORE_PASSWORD=${KEY_STORE_PASSWORD}" JVM_MAX_HEAP=${MAX_HEAP:-1024} diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index 615ad1e..505afbf 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright � 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright � 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,12 @@ */ package org.onap.aai.babel.parser; +import com.google.gson.Gson; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -32,6 +36,7 @@ import java.util.Properties; import java.util.stream.Collectors; import java.util.stream.Stream; import org.onap.aai.babel.logging.LogHelper; +import org.onap.aai.babel.xml.generator.data.GroupConfiguration; 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; @@ -82,7 +87,7 @@ public class ArtifactGeneratorToscaParser { * Constructs using csarHelper * * @param csarHelper - * The csar helper + * The csar helper */ public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { this.csarHelper = csarHelper; @@ -92,7 +97,7 @@ public class ArtifactGeneratorToscaParser { * Get or create the artifact description. * * @param model - * the artifact model + * the artifact model * @return the artifact model's description */ public static String getArtifactDescription(Model model) { @@ -130,26 +135,28 @@ public class ArtifactGeneratorToscaParser { } /** - * Initialises the group filter configuration. + * Initialises the group filtering and mapping configuration. + * + * @throws FileNotFoundException * - * @throws IOException */ - public static void initGroupFilterConfiguration() throws IOException { - log.debug("Getting Filter Tyoes Configuration"); + public static void initGroupFilterConfiguration() throws FileNotFoundException { + log.debug("Getting Filter Types 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 { + if (configLocation == null) { throw new IllegalArgumentException( String.format(GENERATOR_AAI_CONFIGLOCATION_NOT_FOUND, PROPERTY_GROUP_FILTERS_CONFIG_FILE)); } + + File file = new File(configLocation); + if (!file.exists()) { + throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation)); + } + + BufferedReader bufferedReader = new BufferedReader(new FileReader(configLocation)); + GroupConfiguration config = new Gson().fromJson(bufferedReader, GroupConfiguration.class); + WidgetConfigurationUtil.setSupportedInstanceGroups(config.getInstanceGroupTypes()); + WidgetConfigurationUtil.setTypeMappings(config.getToscaToWidgetMappings()); } /** @@ -178,9 +185,9 @@ public class ArtifactGeneratorToscaParser { * duplicate keys then the TOSCA Property value takes precedence. * * @param stringProps - * initial Map of String property values (e.g. from the TOSCA YAML metadata section) + * initial Map of String property values (e.g. from the TOSCA YAML metadata section) * @param toscaProps - * Map of TOSCA Property Type Object values to merge in (or overwrite) + * Map of TOSCA Property Type Object values to merge in (or overwrite) * @return a Map of the property values converted to String */ public Map mergeProperties(Map stringProps, Map toscaProps) { @@ -278,13 +285,13 @@ public class ArtifactGeneratorToscaParser { * Create an Instance Group Model and populate it with the supplied data. * * @param resourceModel - * the Resource node template Model + * the Resource node template Model * @param memberNodes - * the Resources and Widgets belonging to the Group + * the Resources and Widgets belonging to the Group * @param metaProperties - * the metadata of the Group + * the metadata of the Group * @param properties - * the properties of the Group + * the properties of the Group * @return the Instance Group and Member resource models */ private List processInstanceGroup(Model resourceModel, ArrayList memberNodes, @@ -379,7 +386,7 @@ public class ArtifactGeneratorToscaParser { * Create a Map of property name against String property value from the input Map * * @param inputMap - * The input Map + * The input Map * @return Map of property name against String property value */ private Map populateStringProperties(Map inputMap) { @@ -392,13 +399,13 @@ public class ArtifactGeneratorToscaParser { * is ProvidingService return true, otherwise return false. * * @param resourceModel - * parent Resource + * parent Resource * @param metaData - * for populating the Resource IDs + * for populating the Resource IDs * @param resourceNode - * any Model (will be ignored if not a Resource) + * any Model (will be ignored if not a Resource) * @param nodeProperties - * the node properties + * the node properties * @return whether or not a ProvidingService was prcoessed */ private boolean processModel(Model resourceModel, Metadata metaData, Model resourceNode, diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index 531a044..d6d0a1e 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -102,7 +102,7 @@ public class AaiArtifactGenerator implements ArtifactGenerator { * * @param serviceVersion * @param csarHelper - * interface to the TOSCA parser + * interface to the TOSCA parser * @return the generated Artifacts (containing XML models) */ private GenerationData generateAllArtifacts(final String serviceVersion, ISdcCsarHelper csarHelper) { @@ -265,7 +265,7 @@ public class AaiArtifactGenerator implements ArtifactGenerator { * Method to generate the artifact name for an AAI model. * * @param model - * AAI artifact model + * AAI artifact model * @return Model artifact name */ private String getArtifactName(Model model) { @@ -289,9 +289,9 @@ public class AaiArtifactGenerator implements ArtifactGenerator { * Create Resource artifact model from the AAI xml model string. * * @param resourceModel - * Model of the resource artifact + * Model of the resource artifact * @param aaiResourceModel - * AAI model as string + * AAI model as string * @return Generated {@link Artifact} model for the resource */ private Artifact getResourceArtifact(Model resourceModel, String aaiResourceModel) { @@ -321,9 +321,9 @@ public class AaiArtifactGenerator implements ArtifactGenerator { * Create Service artifact model from the AAI XML model. * * @param serviceModel - * Model of the service artifact + * Model of the service artifact * @param aaiServiceModel - * AAI model as string + * AAI model as string * @return Generated {@link Artifact} model for the service */ private Artifact getServiceArtifact(Service serviceModel, String aaiServiceModel) { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java new file mode 100644 index 0000000..9223f27 --- /dev/null +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.xml.generator.data; + +import java.util.List; +import java.util.Map; + +public class GroupConfiguration { + + /** + * Names of Instance Groups that will be processed (not filtered out). + */ + private List instanceGroupTypes; + + /** + * Mapping from TOSCA type to Widget Model. + */ + private Map toscaToWidgetMappings; + + public List getInstanceGroupTypes() { + return instanceGroupTypes; + } + + public Map getToscaToWidgetMappings() { + return toscaToWidgetMappings; + } +} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java index 9f8cbf8..30b6c8e 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,19 @@ */ package org.onap.aai.babel.xml.generator.data; -import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; +import org.onap.aai.babel.xml.generator.model.Model; public class WidgetConfigurationUtil { private static Properties config; private static List instanceGroups = Collections.emptyList(); + private static Map> typeToModel = new HashMap<>(); /* * Private constructor to prevent instantiation @@ -45,14 +49,38 @@ public class WidgetConfigurationUtil { WidgetConfigurationUtil.config = config; } - public static void setFilterConfig(Properties properties) { - String instanceGroupsList = (String) properties.get("AAI.instance-group-types"); - if (instanceGroupsList != null) { - instanceGroups = Arrays.asList(instanceGroupsList.split(",")); - } - } + public static void setSupportedInstanceGroups(List supportedInstanceGroups) { + instanceGroups = supportedInstanceGroups; + } public static boolean isSupportedInstanceGroup(String groupType) { return instanceGroups.contains(groupType); } + + /** + * Create the mappings from TOSCA type to Widget type. The Properties store a set of TOSCA type prefix Strings. + * These keys take a single class name (String), which is used to map to a Widget Class in the Model. + * + * @param map + * the key/value pairs of TOSCA type and Class name + */ + @SuppressWarnings("unchecked") + public static void setTypeMappings(Map map) { + for (Entry entry : map.entrySet()) { + final String toscaType = entry.getKey(); + final String javaBean = entry.getValue(); + final String modelClassName = Model.class.getPackage().getName() + "." + javaBean; + try { + typeToModel.put(toscaType, (Class) Class.forName(modelClassName)); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException( + String.format("Unsupported type \"%s\" for TOSCA mapping %s: no class found for %s", // + javaBean, toscaType, modelClassName)); + } + } + } + + public static Class getModelFromType(String typePrefix) { + return typeToModel.get(typePrefix); + } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java index 7b2fc42..0e2b8d5 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import java.util.Optional; import java.util.Set; 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.error.IllegalAccessException; import org.onap.aai.babel.xml.generator.types.Cardinality; import org.onap.aai.babel.xml.generator.types.ModelType; @@ -40,22 +41,6 @@ public abstract class Model { private static Logger log = LogHelper.INSTANCE; - private static Map> typeToModel = new HashMap<>(); - static { - typeToModel.put("org.openecomp.resource.vf.allottedResource", AllotedResource.class); - typeToModel.put("org.openecomp.resource.vfc.AllottedResource", ProvidingService.class); - typeToModel.put("org.openecomp.resource.vfc", VServerWidget.class); - typeToModel.put("org.openecomp.resource.cp", LIntfWidget.class); - typeToModel.put("org.openecomp.cp", LIntfWidget.class); - typeToModel.put("org.openecomp.resource.vl", L3Network.class); - typeToModel.put("org.openecomp.resource.vf", VirtualFunction.class); - typeToModel.put("org.openecomp.groups.vfmodule", VfModule.class); - typeToModel.put("org.openecomp.groups.VfModule", VfModule.class); - typeToModel.put("org.openecomp.resource.vfc.nodes.heat.cinder", VolumeWidget.class); - typeToModel.put("org.openecomp.nodes.PortMirroringConfiguration", Configuration.class); - typeToModel.put("org.openecomp.resource.cr.Kk1806Cr1", CR.class); - } - private enum ModelIdentification { ID("vfModuleModelInvariantUUID", "serviceInvariantUUID", "resourceInvariantUUID", "invariantUUID", "providing_service_invariant_uuid") { @@ -154,7 +139,7 @@ public abstract class Model { private static Optional getModelFromType(String typePrefix) { Optional modelToBeReturned = Optional.empty(); - Class clazz = typeToModel.get(typePrefix); + Class clazz = WidgetConfigurationUtil.getModelFromType(typePrefix); if (clazz != null) { try { modelToBeReturned = Optional.ofNullable(clazz.getConstructor().newInstance()); @@ -197,8 +182,8 @@ public abstract class Model { * @return the cardinality */ public Cardinality getCardinality() { - org.onap.aai.babel.xml.generator.types.Model model = this.getClass() - .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); return model.cardinality(); } @@ -208,8 +193,8 @@ public abstract class Model { * @return the delete flag */ public boolean getDeleteFlag() { - org.onap.aai.babel.xml.generator.types.Model model = this.getClass() - .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); return model.dataDeleteFlag(); } @@ -258,8 +243,8 @@ public abstract class Model { * @return the widget version id */ public String getWidgetId() { - org.onap.aai.babel.xml.generator.types.Model model = this.getClass() - .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); return Widget.getWidget(model.widget()).getId(); } @@ -269,8 +254,8 @@ public abstract class Model { * @return the invariant id */ public String getWidgetInvariantId() { - org.onap.aai.babel.xml.generator.types.Model model = this.getClass() - .getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); + org.onap.aai.babel.xml.generator.types.Model model = + this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); return Widget.getWidget(model.widget()).getWidgetId(); } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java index d78e2e6..963d9e2 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,8 +109,7 @@ public abstract class Widget extends Model { } public String getId() { - Properties properties = WidgetConfigurationUtil.getConfig(); - String id = properties.getProperty(ArtifactType.AAI.name() + ".model-version-id." + getName()); + String id = WidgetConfigurationUtil.getConfig().getProperty(ArtifactType.AAI.name() + ".model-version-id." + getName()); if (id == null) { throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGLPROP_NOT_FOUND, ArtifactType.AAI.name() + ".model-version-id." + getName())); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2cb4fe8..c9982d6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,6 @@ server.ssl.client-auth=need server.contextPath=/services/babel-service -logging.config=${CONFIG_HOME}/logback.xml \ No newline at end of file +logging.config=${CONFIG_HOME}/logback.xml + +groupfilter.config=${CONFIG_HOME}/tosca-mappings.json diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index b7957f7..52dd462 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; -import java.util.Properties; import org.junit.Test; import org.mockito.Mockito; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; @@ -76,9 +75,7 @@ public class TestArtifactGeneratorToscaParser { @Test public void testInstanceGroups() { final String instanceGroupType = "org.openecomp.groups.ResourceInstanceGroup"; - Properties props = new Properties(); - props.put("AAI.instance-group-types", instanceGroupType); - WidgetConfigurationUtil.setFilterConfig(props); + WidgetConfigurationUtil.setSupportedInstanceGroups(Collections.singletonList(instanceGroupType)); ISdcCsarHelper helper = Mockito.mock(ISdcCsarHelper.class); SubstitutionMappings sm = Mockito.mock(SubstitutionMappings.class); @@ -105,9 +102,9 @@ public class TestArtifactGeneratorToscaParser { * sdc-tosca parser. * * @param name - * name of the NodeTemplate + * name of the NodeTemplate * @param type - * type of the NodeTemplate + * type of the NodeTemplate * @return a new NodeTemplate object */ private NodeTemplate buildNodeTemplate(String name, String type) { diff --git a/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java index 3bab915..f8d8478 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,13 +24,10 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Properties; import org.junit.Before; import org.junit.Test; import org.onap.aai.babel.csar.extractor.InvalidArchiveException; @@ -40,10 +37,9 @@ import org.onap.aai.babel.xml.generator.api.AaiArtifactGenerator; import org.onap.aai.babel.xml.generator.data.AdditionalParams; import org.onap.aai.babel.xml.generator.data.Artifact; import org.onap.aai.babel.xml.generator.data.GenerationData; -import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; /** - * Direct tests of the Model to improve code coverage. + * Direct tests of the {@link AaiArtifactGenerator} to improve code coverage. */ public class TestToscaParser { @@ -53,22 +49,9 @@ public class TestToscaParser { } } - private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; - private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; - @Before - public void setup() throws FileNotFoundException, IOException { - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); - - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); - - InputStream in = TestToscaParser.class.getClassLoader().getResourceAsStream("artifact-generator.properties"); - Properties properties = new Properties(); - properties.load(in); - in.close(); - WidgetConfigurationUtil.setConfig(properties); + public void setup() { + new ArtifactTestUtils().setGeneratorSystemProperties(); } @Test @@ -78,8 +61,8 @@ public class TestToscaParser { additionalParams.put(AdditionalParams.SERVICE_VERSION.getName(), "1.0"); AaiArtifactGenerator generator = new AaiArtifactGenerator(); - GenerationData data = generator.generateArtifact(CsarTest.VNF_VENDOR_CSAR.getContent(), ymlFiles, - additionalParams); + GenerationData data = + generator.generateArtifact(CsarTest.VNF_VENDOR_CSAR.getContent(), ymlFiles, additionalParams); assertThat(data.getErrorData().size(), is(equalTo(0))); assertThat(data.getResultData().size(), is(equalTo(2))); diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 1d39f6d..1ead8e6 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,9 +54,6 @@ import org.xml.sax.SAXException; */ public class CsarToXmlConverterTest { - private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; - private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; - private static final String INCORRECT_CSAR_NAME = "the_name_of_the_csar_file.csar"; private static final String SERVICE_VERSION = "1.0"; @@ -74,12 +71,7 @@ public class CsarToXmlConverterTest { @Before public void setup() { - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); - - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); - + new ArtifactTestUtils().setGeneratorSystemProperties(); converter = new CsarToXmlConverter(); } @@ -118,9 +110,9 @@ public class CsarToXmlConverterTest { * Test that an Exception is thrown when the Artifact Generator properties are not present. * * @throws CsarConverterException - * if there is an error either extracting the YAML files or generating XML artifacts + * if there is an error either extracting the YAML files or generating XML artifacts * @throws IOException - * if an I/O exception occurs loading the test CSAR file + * if an I/O exception occurs loading the test CSAR file * @throws IOException * @throws XmlArtifactGenerationException * @throws CsarConverterException @@ -147,7 +139,8 @@ public class CsarToXmlConverterTest { public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() throws IOException, XmlArtifactGenerationException, CsarConverterException { exception.expect(CsarConverterException.class); - exception.expectMessage("Cannot generate artifacts. System property groupfilter.config not configured"); + exception.expectMessage("Cannot generate artifacts. System property " + + ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE + " not configured"); // Unset the required system property System.clearProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE); diff --git a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java index 78e02f4..d87f3c3 100644 --- a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java +++ b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.service; import static org.hamcrest.Matchers.is; @@ -42,7 +43,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.onap.aai.auth.AAIMicroServiceAuth; -import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser; import org.onap.aai.babel.service.data.BabelRequest; import org.onap.aai.babel.testdata.CsarTest; import org.onap.aai.babel.util.ArtifactTestUtils; @@ -55,7 +55,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "classpath:/babel-beans.xml" }) +@ContextConfiguration(locations = {"classpath:/babel-beans.xml"}) public class TestGenerateArtifactsServiceImpl { static { @@ -65,18 +65,14 @@ public class TestGenerateArtifactsServiceImpl { System.setProperty("CONFIG_HOME", "src/test/resources"); } - private static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; - private static final String FILTER_TYPES_CONFIG = "filter-types.properties"; @Inject private AAIMicroServiceAuth auth; @BeforeClass public static void setup() { - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(ARTIFACT_GENERATOR_CONFIG)); - System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, - new ArtifactTestUtils().getResourcePath(FILTER_TYPES_CONFIG)); + new ArtifactTestUtils().setGeneratorSystemProperties(); + } @Test @@ -199,7 +195,7 @@ public class TestGenerateArtifactsServiceImpl { Mockito.when(mockCertificate.getSubjectX500Principal()) .thenReturn(new X500Principal("CN=test, OU=qa, O=Test Ltd, L=London, ST=London, C=GB")); - servletRequest.setAttribute("javax.servlet.request.X509Certificate", new X509Certificate[] { mockCertificate }); + servletRequest.setAttribute("javax.servlet.request.X509Certificate", new X509Certificate[] {mockCertificate}); servletRequest.setAttribute("javax.servlet.request.cipher_suite", ""); GenerateArtifactsServiceImpl service = new GenerateArtifactsServiceImpl(auth); diff --git a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java index fa0b784..6608c00 100644 --- a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java +++ b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.util; import static org.hamcrest.CoreMatchers.equalTo; @@ -27,6 +28,7 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import java.io.IOException; +import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; @@ -36,14 +38,17 @@ import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; import org.custommonkey.xmlunit.Diff; +import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser; import org.onap.aai.babel.xml.generator.data.Artifact; +import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; import org.xml.sax.SAXException; /** - * This class provides some utilities to assist with running tests. + * This class provides some utilities to assist with running local unit tests. */ public class ArtifactTestUtils { @@ -51,15 +56,33 @@ public class ArtifactTestUtils { private static final String JSON_RESPONSES_FOLDER = "response/"; private static final String CSAR_INPUTS_FOLDER = "compressedArtifacts/"; + public void setGeneratorSystemProperties() { + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, + getResourcePath(Resources.ARTIFACT_GENERATOR_CONFIG)); + + System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE, + getResourcePath(Resources.FILTER_TYPES_CONFIG)); + } + + /** + * Load the Widget to UUID mappings from the Artifact Generator Properties (resource). + * + * @throws IOException + * if the properties file is not loaded + */ + public void loadWidgetToUuidMappings() throws IOException { + WidgetConfigurationUtil.setConfig(getResourceAsProperties(Resources.ARTIFACT_GENERATOR_CONFIG)); + } + /** * Specific test method for the YAML Extractor test. * * @param toscaFiles - * files extracted by the YamlExtractor + * files extracted by the YamlExtractor * @param ymlPayloadsToLoad - * the expected YAML files + * the expected YAML files * @throws IOException - * if an I/O exception occurs + * if an I/O exception occurs */ public void performYmlAsserts(List toscaFiles, List ymlPayloadsToLoad) throws IOException { assertThat("An incorrect number of YAML files have been extracted", toscaFiles.size(), @@ -83,14 +106,14 @@ public class ArtifactTestUtils { * Compare two XML strings to see if they have the same content. * * @param string1 - * XML content + * XML content * @param string2 - * XML content + * XML content * @return true if XML content is similar * @throws IOException - * if an I/O exception occurs + * if an I/O exception occurs * @throws SAXException - * if the XML parsing fails + * if the XML parsing fails */ public boolean compareXmlStrings(String string1, String string2) throws SAXException, IOException { return new Diff(string1, string2).similar(); @@ -120,6 +143,14 @@ public class ArtifactTestUtils { return Files.lines(Paths.get(getResource(resourceFile).toURI())).collect(Collectors.joining()); } + public Properties getResourceAsProperties(String resourceName) throws IOException { + final Properties properties = new Properties(); + InputStream in = ArtifactTestUtils.class.getClassLoader().getResourceAsStream(resourceName); + properties.load(in); + in.close(); + return properties; + } + public String getResourcePath(String resourceName) { return getResource(resourceName).getPath(); } diff --git a/src/test/java/org/onap/aai/babel/util/Resources.java b/src/test/java/org/onap/aai/babel/util/Resources.java new file mode 100644 index 0000000..199b3cd --- /dev/null +++ b/src/test/java/org/onap/aai/babel/util/Resources.java @@ -0,0 +1,31 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.babel.util; + +/** + * Common Test resources. + */ +public class Resources { + + public static final String ARTIFACT_GENERATOR_CONFIG = "artifact-generator.properties"; + public static final String FILTER_TYPES_CONFIG = "tosca-mappings.json"; + +} diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java index 9b5700d..912a505 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,15 +26,13 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.Arrays; import java.util.List; -import java.util.Properties; import org.junit.Before; import org.junit.Test; -import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser; +import org.onap.aai.babel.util.ArtifactTestUtils; import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.aai.babel.xml.generator.types.ModelType; @@ -54,20 +52,19 @@ public class TestModel { } /** - * Load the Widget to UUID mappings from the Artifact Generator properties. + * Initialise the Artifact Generator with filtering and mapping configuration. Also Load the Widget to UUID mappings + * from the Artifact Generator properties. * - * @throws FileNotFoundException - * if the properties file is missing * @throws IOException - * if the properties file is not loaded + * if the Artifact Generator properties file is not loaded */ @Before - public void setup() throws FileNotFoundException, IOException { - InputStream in = TestModel.class.getClassLoader().getResourceAsStream("artifact-generator.properties"); - Properties properties = new Properties(); - properties.load(in); - in.close(); - WidgetConfigurationUtil.setConfig(properties); + public void setup() throws IOException { + ArtifactTestUtils utils = new ArtifactTestUtils(); + utils.setGeneratorSystemProperties(); + + ArtifactGeneratorToscaParser.initGroupFilterConfiguration(); + utils.loadWidgetToUuidMappings(); anonymousModel = new Model() { @Override diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java index e9c8c1b..1a5986b 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,16 +26,13 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertThat; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Properties; import org.junit.BeforeClass; import org.junit.Test; -import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.util.ArtifactTestUtils; import org.onap.aai.babel.xml.generator.model.Widget.Type; /** @@ -47,33 +44,39 @@ public class TestVfModule { System.setProperty("APP_HOME", "."); } - /** - * Load the Widget to UUID mappings from the Artifact Generator properties. - * - * @throws FileNotFoundException if the properties file is missing - * @throws IOException if the properties file is not loaded - */ @BeforeClass - public static void setup() throws FileNotFoundException, IOException { - InputStream in = TestVfModule.class.getClassLoader().getResourceAsStream("artifact-generator.properties"); - Properties properties = new Properties(); - properties.load(in); - in.close(); - WidgetConfigurationUtil.setConfig(properties); + public static void setup() throws IOException { + new ArtifactTestUtils().loadWidgetToUuidMappings(); } /** - * Call equals() and hashCode() methods for code coverage. + * Call hashCode() method for code coverage. */ @Test - public void testEqualsHashCode() { + public void testHashCode() { VfModule vfModule = createNewVfModule(); populateIdentInfo(vfModule); assertThat(vfModule.hashCode(), is(notNullValue())); - assertThat(vfModule.equals(vfModule), is(true)); - // Tests that the overridden equals() method correctly returns false for a different type of Object - // This is necessary to achieve complete code coverage - assertThat(vfModule.equals("string"), is(false)); // NOSONAR + } + + /** + * Call equals() method for code coverage. + */ + @Test + public void testEquals() { + VfModule vfModuleA = createNewVfModule(); + populateIdentInfo(vfModuleA); + + // equals() is reflexive + assertThat(vfModuleA.equals(vfModuleA), is(true)); + + // equals() is symmetric + VfModule vfModuleB = createNewVfModule(); + populateIdentInfo(vfModuleB); + assertThat(vfModuleA.equals(vfModuleB), is(true)); + assertThat(vfModuleB.equals(vfModuleA), is(true)); + + assertThat(vfModuleA.equals(null), is(false)); } @Test @@ -216,7 +219,8 @@ public class TestVfModule { /** * Use the static Factory method to create a new Widget. * - * @param widgetType type of Widget to create + * @param widgetType + * type of Widget to create * @return a new Widget */ private Widget createNewWidget(Type widgetType) { @@ -237,7 +241,8 @@ public class TestVfModule { /** * Set up some dummy Model Identification properties. * - * @param vfModule to be populated + * @param vfModule + * to be populated */ private void populateIdentInfo(VfModule vfModule) { Map modelIdentInfo = new HashMap<>(); @@ -248,8 +253,10 @@ public class TestVfModule { /** * Create a new Widget and assert that it is successfully added to the VF Module. * - * @param vfModule the VF Module to update - * @param widgetType the type of Widget to create and add + * @param vfModule + * the VF Module to update + * @param widgetType + * the type of Widget to create and add */ private void assertAddWidget(VfModule vfModule, Type widgetType) { assertThat(createNewWidgetForModule(vfModule, widgetType), is(true)); @@ -258,8 +265,10 @@ public class TestVfModule { /** * Create a new Widget and assert that it cannot be added to the VF Module. * - * @param vfModule the VF Module - * @param widgetType the type of Widget to create and attempt to add + * @param vfModule + * the VF Module + * @param widgetType + * the type of Widget to create and attempt to add */ private void assertFailToAddWidget(VfModule vfModule, Type widgetType) { assertThat(createNewWidgetForModule(vfModule, widgetType), is(false)); @@ -268,8 +277,10 @@ public class TestVfModule { /** * Create a new widget, make it a member of the VF Module, then try to add it. * - * @param vfModule the VF Module to update - * @param widgetType the type of Widget to create and attempt to add + * @param vfModule + * the VF Module to update + * @param widgetType + * the type of Widget to create and attempt to add * @return whether or not the Widget was added to the module */ private boolean createNewWidgetForModule(VfModule vfModule, Type widgetType) { @@ -283,8 +294,10 @@ public class TestVfModule { * to its set of keys, and by then setting the VF Module's members to a Singleton List comprised of this ID. These * updates allow the Widget to be successfully added to the VF Module. (Non-member Widgets cannot be added.) * - * @param vfModule the module for which members are overwritten - * @param widget the widget to be set as the member + * @param vfModule + * the module for which members are overwritten + * @param widget + * the widget to be set as the member */ private void setWidgetAsMember(VfModule vfModule, Widget widget) { String id = widget.getId(); @@ -295,7 +308,8 @@ public class TestVfModule { /** * Create a vserver widget and add it to the specified VF Module. * - * @param vfModule the VF Module to update + * @param vfModule + * the VF Module to update * @return the number of Widgets present in the vserver on creation */ private int createVserverForVf(VfModule vfModule) { @@ -309,8 +323,10 @@ public class TestVfModule { /** * Add the specified vserver to the specified VF Module. * - * @param vfModule the VF Module to update - * @param vserverWidget the Widget to add + * @param vfModule + * the VF Module to update + * @param vserverWidget + * the Widget to add * @return initial widget count for the vserver Widget */ private int addVserverToVf(VfModule vfModule, VServerWidget vserverWidget) { diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java index 212c221..d7fe4af 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestWidget.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,14 +26,11 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; import java.util.Collections; -import java.util.Properties; import org.junit.BeforeClass; import org.junit.Test; -import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; +import org.onap.aai.babel.util.ArtifactTestUtils; import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.aai.babel.xml.generator.types.ModelType; @@ -49,18 +46,12 @@ public class TestWidget { /** * Load the Widget to UUID mappings from the Artifact Generator properties. * - * @throws FileNotFoundException - * if the properties file is missing * @throws IOException * if the properties file is not loaded */ @BeforeClass - public static void setup() throws FileNotFoundException, IOException { - final Properties properties = new Properties(); - try (InputStream in = TestWidget.class.getClassLoader().getResourceAsStream("artifact-generator.properties")) { - properties.load(in); - } - WidgetConfigurationUtil.setConfig(properties); + public static void setup() throws IOException { + new ArtifactTestUtils().loadWidgetToUuidMappings(); } @Test diff --git a/src/test/resources/filter-types.properties b/src/test/resources/filter-types.properties deleted file mode 100644 index 8577841..0000000 --- a/src/test/resources/filter-types.properties +++ /dev/null @@ -1 +0,0 @@ -AAI.instance-group-types=org.openecomp.groups.NetworkCollection,org.openecomp.groups.VfcInstanceGroup,org.openecomp.groups.ResourceInstanceGroup diff --git a/src/test/resources/tosca-mappings.json b/src/test/resources/tosca-mappings.json new file mode 100644 index 0000000..9c3d0b4 --- /dev/null +++ b/src/test/resources/tosca-mappings.json @@ -0,0 +1,21 @@ +{ + "instanceGroupTypes": [ + "org.openecomp.groups.NetworkCollection", + "org.openecomp.groups.VfcInstanceGroup", + "org.openecomp.groups.ResourceInstanceGroup" + ], + "toscaToWidgetMappings": { + "org.openecomp.resource.vf.allottedResource": "AllotedResource", + "org.openecomp.resource.vfc.AllottedResource": "ProvidingService", + "org.openecomp.resource.vfc": "VServerWidget", + "org.openecomp.resource.cp": "LIntfWidget", + "org.openecomp.cp": "LIntfWidget", + "org.openecomp.resource.vl": "L3Network", + "org.openecomp.resource.vf": "VirtualFunction", + "org.openecomp.groups.vfmodule": "VfModule", + "org.openecomp.groups.VfModule": "VfModule", + "org.openecomp.resource.vfc.nodes.heat.cinder": "VolumeWidget", + "org.openecomp.nodes.PortMirroringConfiguration": "Configuration", + "org.openecomp.resource.cr.Kk1806Cr1": "CR" + } +} -- 2.16.6 From 1954294aed95c2db4eb2659dcef91248535de079 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Tue, 5 Feb 2019 14:24:33 +0000 Subject: [PATCH 15/16] Replace Resource sub-classes with configuration Delete the sub-classes of the Resource (internal Model class) and replace the annotated properties of these classes with TOSCA-to-Model mappping data loaded from the JSON configuration. Change-Id: I56d5d9d4893be45eb42ae65099ea9abe5f4409b9 Issue-ID: AAI-2121 Signed-off-by: mark.j.leonard --- .../babel/parser/ArtifactGeneratorToscaParser.java | 144 ++++++++++----------- .../xml/generator/api/AaiArtifactGenerator.java | 43 ++++-- .../xml/generator/data/GroupConfiguration.java | 12 +- .../generator/data/WidgetConfigurationUtil.java | 40 ++---- .../WidgetMapping.java} | 22 ++-- .../babel/xml/generator/model/AllotedResource.java | 28 ---- .../org/onap/aai/babel/xml/generator/model/CR.java | 28 ---- .../babel/xml/generator/model/Configuration.java | 28 ---- .../babel/xml/generator/model/InstanceGroup.java | 28 ---- .../aai/babel/xml/generator/model/L3Network.java | 28 ---- .../onap/aai/babel/xml/generator/model/Model.java | 63 +++------ .../xml/generator/model/ProvidingService.java | 28 ---- .../aai/babel/xml/generator/model/Resource.java | 125 ++++++++++++++++-- .../aai/babel/xml/generator/model/Service.java | 12 ++ .../aai/babel/xml/generator/model/VfModule.java | 109 ---------------- .../babel/xml/generator/model/VirtualFunction.java | 28 ---- .../onap/aai/babel/xml/generator/model/Widget.java | 42 +++--- .../parser/TestArtifactGeneratorToscaParser.java | 16 ++- .../aai/babel/xml/generator/model/TestModel.java | 83 +++++++----- .../babel/xml/generator/model/TestVfModule.java | 44 +++---- src/test/resources/tosca-mappings.json | 70 +++++++--- 21 files changed, 442 insertions(+), 579 deletions(-) rename src/main/java/org/onap/aai/babel/xml/generator/{logging/CategoryLogLevel.java => data/WidgetMapping.java} (70%) delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/CR.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/Configuration.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/InstanceGroup.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java delete mode 100644 src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java diff --git a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java index 505afbf..7dfc807 100644 --- a/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java +++ b/src/main/java/org/onap/aai/babel/parser/ArtifactGeneratorToscaParser.java @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.parser; import com.google.gson.Gson; @@ -38,14 +39,10 @@ import java.util.stream.Stream; import org.onap.aai.babel.logging.LogHelper; import org.onap.aai.babel.xml.generator.data.GroupConfiguration; 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; import org.onap.aai.babel.xml.generator.model.Resource; -import org.onap.aai.babel.xml.generator.model.VfModule; import org.onap.aai.babel.xml.generator.model.Widget; +import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.aai.cl.api.Logger; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.toscaparser.api.Group; @@ -87,7 +84,7 @@ public class ArtifactGeneratorToscaParser { * Constructs using csarHelper * * @param csarHelper - * The csar helper + * The csar helper */ public ArtifactGeneratorToscaParser(ISdcCsarHelper csarHelper) { this.csarHelper = csarHelper; @@ -97,7 +94,7 @@ public class ArtifactGeneratorToscaParser { * Get or create the artifact description. * * @param model - * the artifact model + * the artifact model * @return the artifact model's description */ public static String getArtifactDescription(Model model) { @@ -152,11 +149,11 @@ public class ArtifactGeneratorToscaParser { if (!file.exists()) { throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGFILE_NOT_FOUND, configLocation)); } - + BufferedReader bufferedReader = new BufferedReader(new FileReader(configLocation)); GroupConfiguration config = new Gson().fromJson(bufferedReader, GroupConfiguration.class); WidgetConfigurationUtil.setSupportedInstanceGroups(config.getInstanceGroupTypes()); - WidgetConfigurationUtil.setTypeMappings(config.getToscaToWidgetMappings()); + WidgetConfigurationUtil.setWidgetMappings(config.getWidgetMappings()); } /** @@ -185,9 +182,9 @@ public class ArtifactGeneratorToscaParser { * duplicate keys then the TOSCA Property value takes precedence. * * @param stringProps - * initial Map of String property values (e.g. from the TOSCA YAML metadata section) + * initial Map of String property values (e.g. from the TOSCA YAML metadata section) * @param toscaProps - * Map of TOSCA Property Type Object values to merge in (or overwrite) + * Map of TOSCA Property Type Object values to merge in (or overwrite) * @return a Map of the property values converted to String */ public Map mergeProperties(Map stringProps, Map toscaProps) { @@ -198,7 +195,7 @@ public class ArtifactGeneratorToscaParser { } public Resource createInstanceGroupModel(Map properties) { - Resource groupModel = new InstanceGroup(); + Resource groupModel = new Resource(Type.INSTANCE_GROUP, true); groupModel.populateModelIdentificationInformation(properties); return groupModel; } @@ -207,28 +204,14 @@ public class ArtifactGeneratorToscaParser { * @param model * @param relation */ - public void addRelatedModel(final Model model, final Model relation) { - if (relation instanceof Resource) { - model.addResource((Resource) relation); + public void addRelatedModel(final Model model, final Resource relation) { + if (relation.isResource()) { + model.addResource(relation); } else { - model.addWidget((Widget) relation); + model.addWidget(Widget.getWidget(relation.getWidgetType())); } } - public 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.")) { - nodeTypeName = "org.openecomp.resource.vf.allottedResource"; - } - if (nodeType.getType().contains("org.openecomp.resource.vfc.")) { - nodeTypeName = "org.openecomp.resource.vfc.AllottedResource"; - } - } - return nodeTypeName; - } - public boolean hasAllottedResource(Map metadata) { return ALLOTTED_RESOURCE.equals(metadata.get(CATEGORY)); } @@ -252,8 +235,8 @@ public class ArtifactGeneratorToscaParser { // Process each VF Group for (Group serviceGroup : serviceGroups) { Model groupModel = Model.getModelFor(serviceGroup.getType()); - if (groupModel instanceof VfModule) { - processVfModule(resources, resourceModel, serviceGroup, serviceNode, (VfModule) groupModel); + if (groupModel.getWidgetType() == Type.VFMODULE) { + processVfModule(resources, resourceModel, serviceGroup, serviceNode, (Resource) groupModel); } } } @@ -266,15 +249,24 @@ public class ArtifactGeneratorToscaParser { boolean foundProvidingService = false; for (NodeTemplate resourceNodeTemplate : resourceNodeTemplates) { - 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(resourceModel, metaData, resourceNode, resourceNodeTemplate.getProperties()); + String nodeTypeName = resourceNodeTemplate.getType(); + Metadata metadata = resourceNodeTemplate.getMetaData(); + String metaDataType = Optional.ofNullable(metadata).map(m -> m.getValue("type")).orElse(nodeTypeName); + Resource model = Model.getModelFor(nodeTypeName, metaDataType); + + if (metadata != null && hasAllottedResource(metadata.getAllProperties())) { + if (model.getWidgetType() == Type.VSERVER) { + model = new Resource(Type.ALLOTTED_RESOURCE, false); + Map props = new HashMap<>(); + props.put("providingService", true); + model.setProperties(props); + } + } + + foundProvidingService |= processModel(resourceModel, metadata, model, resourceNodeTemplate.getProperties()); } - if (resourceModel instanceof AllotedResource && !foundProvidingService) { + if (resourceModel.getWidgetType() == Type.ALLOTTED_RESOURCE && !foundProvidingService) { final String modelInvariantId = resourceModel.getModelId(); throw new IllegalArgumentException(String.format(GENERATOR_AAI_PROVIDING_SERVICE_MISSING, modelInvariantId == null ? "" : modelInvariantId)); @@ -285,13 +277,13 @@ public class ArtifactGeneratorToscaParser { * Create an Instance Group Model and populate it with the supplied data. * * @param resourceModel - * the Resource node template Model + * the Resource node template Model * @param memberNodes - * the Resources and Widgets belonging to the Group + * the Resources and Widgets belonging to the Group * @param metaProperties - * the metadata of the Group + * the metadata of the Group * @param properties - * the properties of the Group + * the properties of the Group * @return the Instance Group and Member resource models */ private List processInstanceGroup(Model resourceModel, ArrayList memberNodes, @@ -320,11 +312,11 @@ public class ArtifactGeneratorToscaParser { List resources = new ArrayList<>(); for (NodeTemplate nodeTemplate : memberNodes) { - String nodeTypeName = normaliseNodeTypeName(nodeTemplate); + String nodeTypeName = nodeTemplate.getType(); final String metadataType = nodeTemplate.getMetaData().getValue("type"); log.debug(String.format("Get model for %s (metadata type %s)", nodeTypeName, metadataType)); - Model memberModel = Model.getModelFor(nodeTypeName, metadataType); + Resource memberModel = Model.getModelFor(nodeTypeName, metadataType); if (memberModel != null) { memberModel.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); @@ -333,8 +325,8 @@ public class ArtifactGeneratorToscaParser { memberModel.getClass().getSuperclass().getSimpleName(), memberModel.getClass(), nodeTypeName)); addRelatedModel(groupModel, memberModel); - if (memberModel instanceof Resource) { - resources.add((Resource) memberModel); + if (memberModel.isResource()) { + resources.add(memberModel); } } } @@ -342,7 +334,7 @@ public class ArtifactGeneratorToscaParser { } private void processVfModule(List resources, Model vfModel, Group groupDefinition, - NodeTemplate serviceNode, VfModule groupModel) { + NodeTemplate serviceNode, Resource groupModel) { groupModel.populateModelIdentificationInformation( mergeProperties(groupDefinition.getMetadata().getAllProperties(), groupDefinition.getProperties())); @@ -355,7 +347,7 @@ public class ArtifactGeneratorToscaParser { } } - private void processVfModuleGroup(VfModule groupModel, List members) { + private void processVfModuleGroup(Resource groupModel, List members) { if (members != null && !members.isEmpty()) { // Get names of the members of the service group List memberNames = members.stream().map(NodeTemplate::getName).collect(Collectors.toList()); @@ -366,16 +358,22 @@ public class ArtifactGeneratorToscaParser { } } - private void processGroupMembers(Model group, NodeTemplate member) { - Model resourceNode; - // L3-network inside vf-module to be generated as Widget a special handling. - if (member.getType().contains("org.openecomp.resource.vl")) { - resourceNode = new L3NetworkWidget(); - } else { - resourceNode = Model.getModelFor(member.getType()); + /** + * Process the Widget members of a VF Module Group + * + * @param group + * @param member + */ + private void processGroupMembers(Resource group, NodeTemplate member) { + Resource resource = Model.getModelFor(member.getType()); + + if (resource.getWidgetType() == Type.L3_NET) { + // An l3-network inside a vf-module is treated as a Widget + resource.setIsResource(false); } - if (resourceNode != null && !(resourceNode instanceof Resource)) { - Widget widget = (Widget) resourceNode; + + if (!resource.isResource()) { + Widget widget = Widget.getWidget(resource.getWidgetType()); widget.addKey(member.getName()); // Add the widget element encountered to the Group model group.addWidget(widget); @@ -386,7 +384,7 @@ public class ArtifactGeneratorToscaParser { * Create a Map of property name against String property value from the input Map * * @param inputMap - * The input Map + * The input Map * @return Map of property name against String property value */ private Map populateStringProperties(Map inputMap) { @@ -396,25 +394,27 @@ public class ArtifactGeneratorToscaParser { /** * If the specified resourceNode is a type of Resource, add it to the specified resourceModel. If the Resource type - * is ProvidingService return true, otherwise return false. + * is ProvidingService then return true, otherwise return false. * * @param resourceModel - * parent Resource + * parent Resource * @param metaData - * for populating the Resource IDs + * for populating the Resource IDs * @param resourceNode - * any Model (will be ignored if not a Resource) + * any Model (will be ignored if not a Resource) * @param nodeProperties - * the node properties - * @return whether or not a ProvidingService was prcoessed + * the node properties + * @return whether or not a ProvidingService was processed */ - private boolean processModel(Model resourceModel, Metadata metaData, Model resourceNode, + private boolean processModel(Model resourceModel, Metadata metaData, Resource resourceNode, Map nodeProperties) { - boolean foundProvidingService = false; - if (resourceNode instanceof ProvidingService) { - foundProvidingService = true; + boolean foundProvidingService = resourceNode != null + && (boolean) Optional.ofNullable(resourceNode.getProperties().get("providingService")).orElse(false); + + if (foundProvidingService) { processProvidingService(resourceModel, resourceNode, nodeProperties); - } else if (resourceNode instanceof Resource && !(resourceNode.getWidgetType().equals(Widget.Type.L3_NET))) { + } else if (resourceNode != null && resourceNode.isResource() + && resourceNode.getWidgetType() != Widget.Type.L3_NET) { if (metaData != null) { resourceNode.populateModelIdentificationInformation(metaData.getAllProperties()); } @@ -423,7 +423,7 @@ public class ArtifactGeneratorToscaParser { return foundProvidingService; } - private void processProvidingService(Model resourceModel, Model resourceNode, + private void processProvidingService(Model resourceModel, Resource resourceNode, Map nodeProperties) { if (nodeProperties == null || nodeProperties.get("providing_service_uuid") == null || nodeProperties.get("providing_service_invariant_uuid") == null) { @@ -433,6 +433,6 @@ public class ArtifactGeneratorToscaParser { Map properties = populateStringProperties(nodeProperties); properties.put(VERSION, "1.0"); resourceNode.populateModelIdentificationInformation(properties); - resourceModel.addResource((Resource) resourceNode); + resourceModel.addResource(resourceNode); } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java index d6d0a1e..fc5acda 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiArtifactGenerator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.xml.generator.api; import java.io.IOException; @@ -26,6 +27,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.onap.aai.babel.logging.ApplicationMsgs; @@ -39,15 +41,16 @@ import org.onap.aai.babel.xml.generator.data.GeneratorUtil; import org.onap.aai.babel.xml.generator.data.GroupType; import org.onap.aai.babel.xml.generator.data.WidgetConfigurationUtil; import org.onap.aai.babel.xml.generator.model.Model; -import org.onap.aai.babel.xml.generator.model.ProvidingService; import org.onap.aai.babel.xml.generator.model.Resource; import org.onap.aai.babel.xml.generator.model.Service; import org.onap.aai.babel.xml.generator.model.TunnelXconnectWidget; +import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.aai.cl.api.Logger; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.tosca.parser.impl.SdcToscaParserFactory; import org.onap.sdc.toscaparser.api.Group; import org.onap.sdc.toscaparser.api.NodeTemplate; +import org.onap.sdc.toscaparser.api.elements.Metadata; import org.slf4j.MDC; public class AaiArtifactGenerator implements ArtifactGenerator { @@ -128,7 +131,10 @@ public class AaiArtifactGenerator implements ArtifactGenerator { for (Resource resource : resources) { generateResourceArtifact(generationData, resource); for (Resource childResource : resource.getResources()) { - if (!(childResource instanceof ProvidingService)) { + boolean isProvidingService = + (boolean) Optional.ofNullable(childResource.getProperties().get("providingService")) // + .orElse(false); + if (!isProvidingService) { generateResourceArtifact(generationData, childResource); } } @@ -179,16 +185,16 @@ public class AaiArtifactGenerator implements ArtifactGenerator { private void generateModelFromNodeTemplate(ISdcCsarHelper csarHelper, Service serviceModel, List resources, final List serviceGroups, ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) { - String nodeTypeName = parser.normaliseNodeTypeName(nodeTemplate); - Model model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + Resource model = getModelFor(parser, nodeTemplate); + if (model != null) { if (nodeTemplate.getMetaData() != null) { model.populateModelIdentificationInformation(nodeTemplate.getMetaData().getAllProperties()); } parser.addRelatedModel(serviceModel, model); - if (model instanceof Resource) { - generateResourceModel(csarHelper, resources, parser, nodeTemplate, nodeTypeName); + if (model.isResource()) { + generateResourceModel(csarHelper, resources, parser, nodeTemplate); } } else { for (Group group : serviceGroups) { @@ -206,11 +212,26 @@ public class AaiArtifactGenerator implements ArtifactGenerator { } } - private void generateResourceModel(ISdcCsarHelper csarHelper, List resources, - ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate, String nodeTypeName) { + private Resource getModelFor(ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) { + String nodeTypeName = nodeTemplate.getType(); + log.debug("Processing resource " + nodeTypeName + ": " + nodeTemplate.getMetaData().getValue("UUID")); - Model resourceModel = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + Resource model = Model.getModelFor(nodeTypeName, nodeTemplate.getMetaData().getValue("type")); + + Metadata metadata = nodeTemplate.getMetaData(); + if (metadata != null && parser.hasAllottedResource(metadata.getAllProperties())) { + if (model.getWidgetType() == Type.VF) { + model = new Resource(Type.ALLOTTED_RESOURCE, true); + } + } + + return model; + } + + private void generateResourceModel(ISdcCsarHelper csarHelper, List resources, + ArtifactGeneratorToscaParser parser, NodeTemplate nodeTemplate) { + Resource resourceModel = getModelFor(parser, nodeTemplate); Map serviceMetadata = nodeTemplate.getMetaData().getAllProperties(); resourceModel.populateModelIdentificationInformation(serviceMetadata); diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java index 9223f27..bbc7164 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/GroupConfiguration.java @@ -18,10 +18,10 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.xml.generator.data; import java.util.List; -import java.util.Map; public class GroupConfiguration { @@ -29,17 +29,17 @@ public class GroupConfiguration { * Names of Instance Groups that will be processed (not filtered out). */ private List instanceGroupTypes; - + /** - * Mapping from TOSCA type to Widget Model. + * Mapping from TOSCA type to Widget directly. */ - private Map toscaToWidgetMappings; + private List widgetMappings; public List getInstanceGroupTypes() { return instanceGroupTypes; } - public Map getToscaToWidgetMappings() { - return toscaToWidgetMappings; + public List getWidgetMappings() { + return widgetMappings; } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java index 30b6c8e..9af6e8d 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetConfigurationUtil.java @@ -24,15 +24,16 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.Optional; import java.util.Properties; -import org.onap.aai.babel.xml.generator.model.Model; +import org.onap.aai.babel.xml.generator.model.Resource; +import org.onap.aai.babel.xml.generator.model.Widget; public class WidgetConfigurationUtil { private static Properties config; private static List instanceGroups = Collections.emptyList(); - private static Map> typeToModel = new HashMap<>(); + private static Map typeToWidget = new HashMap<>(); /* * Private constructor to prevent instantiation @@ -51,36 +52,21 @@ public class WidgetConfigurationUtil { public static void setSupportedInstanceGroups(List supportedInstanceGroups) { instanceGroups = supportedInstanceGroups; - } + } public static boolean isSupportedInstanceGroup(String groupType) { return instanceGroups.contains(groupType); } - /** - * Create the mappings from TOSCA type to Widget type. The Properties store a set of TOSCA type prefix Strings. - * These keys take a single class name (String), which is used to map to a Widget Class in the Model. - * - * @param map - * the key/value pairs of TOSCA type and Class name - */ - @SuppressWarnings("unchecked") - public static void setTypeMappings(Map map) { - for (Entry entry : map.entrySet()) { - final String toscaType = entry.getKey(); - final String javaBean = entry.getValue(); - final String modelClassName = Model.class.getPackage().getName() + "." + javaBean; - try { - typeToModel.put(toscaType, (Class) Class.forName(modelClassName)); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException( - String.format("Unsupported type \"%s\" for TOSCA mapping %s: no class found for %s", // - javaBean, toscaType, modelClassName)); - } - } + public static Optional createModelFromType(String typePrefix) { + return Optional.ofNullable(typeToWidget.get(typePrefix)); } - public static Class getModelFromType(String typePrefix) { - return typeToModel.get(typePrefix); + public static void setWidgetMappings(List mappings) { + for (WidgetMapping mapping : mappings) { + Resource resource = new Resource(Widget.Type.valueOf(mapping.widget), mapping.deleteFlag); + resource.setIsResource(mapping.type.equalsIgnoreCase("resource")); + typeToWidget.put(mapping.prefix, resource); + } } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetMapping.java similarity index 70% rename from src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java rename to src/main/java/org/onap/aai/babel/xml/generator/data/WidgetMapping.java index b6ca2d3..2157728 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/logging/CategoryLogLevel.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/data/WidgetMapping.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,16 @@ * limitations under the License. * ============LICENSE_END========================================================= */ -package org.onap.aai.babel.xml.generator.logging; -public enum CategoryLogLevel { - INFO, - WARN, - DEBUG, - ERROR, - FATAL +package org.onap.aai.babel.xml.generator.data; + +import java.util.Map; + +public class WidgetMapping { + + String prefix; + String type = "resource"; // Default type is Resource (not Widget) + String widget; + boolean deleteFlag; + Map properties; } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java deleted file mode 100644 index cecf7bd..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/AllotedResource.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.ALLOTTED_RESOURCE, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class AllotedResource extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/CR.java b/src/main/java/org/onap/aai/babel/xml/generator/model/CR.java deleted file mode 100644 index 2c50a0f..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/CR.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.CR, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class CR extends Resource { -} \ No newline at end of file diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Configuration.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Configuration.java deleted file mode 100644 index 20dbea9..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Configuration.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.CONFIGURATION, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class Configuration extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/InstanceGroup.java b/src/main/java/org/onap/aai/babel/xml/generator/model/InstanceGroup.java deleted file mode 100644 index f312064..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/InstanceGroup.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.INSTANCE_GROUP, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class InstanceGroup extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java b/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java deleted file mode 100644 index 105c3a1..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/L3Network.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; - -@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.L3_NET, cardinality = Cardinality.UNBOUNDED, - dataDeleteFlag = false) -public class L3Network extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java index 0e2b8d5..d4da6df 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Model.java @@ -27,20 +27,15 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.Set; -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.error.IllegalAccessException; -import org.onap.aai.babel.xml.generator.types.Cardinality; +import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.aai.babel.xml.generator.types.ModelType; -import org.onap.aai.cl.api.Logger; public abstract class Model { public static final String GENERATOR_AAI_ERROR_UNSUPPORTED_WIDGET_OPERATION = "Operation Not Supported for Widgets"; - private static Logger log = LogHelper.INSTANCE; - private enum ModelIdentification { ID("vfModuleModelInvariantUUID", "serviceInvariantUUID", "resourceInvariantUUID", "invariantUUID", "providing_service_invariant_uuid") { @@ -117,37 +112,28 @@ public abstract class Model { * Gets the object (model) corresponding to the supplied TOSCA type. * * @param toscaType - * the tosca type + * the tosca type * @return the model for the type, or null */ - public static Model getModelFor(String toscaType) { - Model model = null; + public static Resource getModelFor(String toscaType) { + Resource resource = null; if (toscaType != null && !toscaType.isEmpty()) { - model = getModelFromType(toscaType).orElseGet(() -> Model.getModelFromPrefix(toscaType)); + resource = getModelFromType(toscaType).orElseGet(() -> Model.getModelFromPrefix(toscaType)); } - return model; + return resource; } - private static Model getModelFromPrefix(String toscaType) { - Model model = null; + private static Resource getModelFromPrefix(String toscaType) { + Resource resource = null; int lastSeparator = toscaType.lastIndexOf('.'); if (lastSeparator != -1) { - model = getModelFor(toscaType.substring(0, lastSeparator)); + resource = getModelFor(toscaType.substring(0, lastSeparator)); } - return model; + return resource; } - private static Optional getModelFromType(String typePrefix) { - Optional modelToBeReturned = Optional.empty(); - Class clazz = WidgetConfigurationUtil.getModelFromType(typePrefix); - if (clazz != null) { - try { - modelToBeReturned = Optional.ofNullable(clazz.getConstructor().newInstance()); - } catch (Exception e) { - log.error(ApplicationMsgs.INVALID_CSAR_FILE, e); - } - } - return modelToBeReturned; + private static Optional getModelFromType(String typePrefix) { + return WidgetConfigurationUtil.createModelFromType(typePrefix); } /** @@ -155,16 +141,16 @@ public abstract class Model { * information. * * @param toscaType - * the TOSCA type + * the TOSCA type * @param metaDataType - * the type from the TOSCA metadata + * the type from the TOSCA metadata * @return the model for the type, or null */ - public static Model getModelFor(String toscaType, String metaDataType) { + public static Resource getModelFor(String toscaType, String metaDataType) { if ("Configuration".equals(metaDataType)) { - return new Configuration(); + return new Resource(Type.CONFIGURATION, true); } else if ("CR".equals(metaDataType)) { - return new CR(); + return new Resource(Type.CR, true); } else { return getModelFor(toscaType); } @@ -176,16 +162,9 @@ public abstract class Model { public abstract Widget.Type getWidgetType(); - /** - * Gets cardinality. - * - * @return the cardinality - */ - public Cardinality getCardinality() { - org.onap.aai.babel.xml.generator.types.Model model = - this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); - return model.cardinality(); - } + public abstract Map getProperties(); + + public abstract boolean isResource(); /** * Gets delete flag. @@ -263,7 +242,7 @@ public abstract class Model { * Populate model identification information. * * @param modelIdentInfo - * the model ident info + * the model ident info */ public void populateModelIdentificationInformation(Map modelIdentInfo) { Iterator iter = modelIdentInfo.keySet().iterator(); diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java b/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java deleted file mode 100644 index e25274e..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/ProvidingService.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; - -@org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.ALLOTTED_RESOURCE, - cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = false) -public class ProvidingService extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java index 1b64907..121bc19 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,28 @@ */ package org.onap.aai.babel.xml.generator.model; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.onap.aai.babel.xml.generator.model.Widget.Type; + public class Resource extends Model { + private Type type; + private boolean deleteFlag; + private boolean isResource = true; + private Map properties = Collections.emptyMap(); + + Widget vserver = null; + boolean addlintf = false; + boolean addvolume = false; + List members; + + public Resource(Type type, boolean deleteFlag) { + this.type = type; + this.deleteFlag = deleteFlag; + } + @Override public int hashCode() { final String uuid = getModelNameVersionId(); @@ -36,20 +56,109 @@ public class Resource extends Model { return false; } + public boolean getDeleteFlag() { + return deleteFlag; + } + + public String getWidgetInvariantId() { + return Widget.getWidget(getWidgetType()).getWidgetId(); + } + + public String getWidgetId() { + return Widget.getWidget(getWidgetType()).getId(); + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + @Override + public Map getProperties() { + return properties; + } + + public void setIsResource(boolean isResource) { + this.isResource = isResource; + } + + @Override + public boolean isResource() { + return isResource; + } + + public void setMembers(List members) { + this.members = members; + } + @Override public boolean addResource(Resource resource) { return resources.add(resource); } + /** + * Adds a Widget. + * + * @param widget + * the widget + * @return the boolean + */ @Override public boolean addWidget(Widget widget) { - return widgets.add(widget); + if (type == Type.VFMODULE) { + if (widget.memberOf(members)) { + if (vserver == null && widget.getId().equals(new VServerWidget().getId())) { + addVserverWidget(widget); + } else if (widget.getId().equals(new LIntfWidget().getId())) { + return addLIntfWidget(widget); + } else if (widget.getId().equals(new VolumeWidget().getId())) { + addVolumeWidget(widget); + return true; + } + if (widget.getId().equals(new OamNetwork().getId())) { + return false; + } + return widgets.add(widget); + } + return false; + } else { + return widgets.add(widget); + } } - @Override - public Widget.Type getWidgetType() { - org.onap.aai.babel.xml.generator.types.Model model = - this.getClass().getAnnotation(org.onap.aai.babel.xml.generator.types.Model.class); - return model.widget(); + public Type getWidgetType() { + return type; + } + + private void addVolumeWidget(Widget widget) { + if (vserver != null) { + vserver.addWidget(widget); + } else { + addvolume = true; + } } + + /** + * @param widget + * @return + */ + private boolean addLIntfWidget(Widget widget) { + if (vserver != null) { + vserver.addWidget(widget); + return true; + } else { + addlintf = true; + return false; + } + } + + private void addVserverWidget(Widget widget) { + vserver = widget; + if (addlintf) { + vserver.addWidget(new LIntfWidget()); + } + if (addvolume) { + vserver.addWidget(new VolumeWidget()); + } + } + } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java index 9d22e41..0815a61 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Service.java @@ -20,6 +20,8 @@ */ package org.onap.aai.babel.xml.generator.model; +import java.util.Collections; +import java.util.Map; import org.onap.aai.babel.xml.generator.types.Cardinality; @org.onap.aai.babel.xml.generator.types.Model(widget = Widget.Type.SERVICE, cardinality = Cardinality.UNBOUNDED, @@ -40,4 +42,14 @@ public class Service extends Model { public Widget.Type getWidgetType() { return null; } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + + @Override + public boolean isResource() { + return false; + } } diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java deleted file mode 100644 index d6d3a2d..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/VfModule.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import java.util.List; -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.VFMODULE, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class VfModule extends Resource { - - Widget vserver = null; - boolean addlintf = false; - boolean addvolume = false; - - List members; - - public void setMembers(List members) { - this.members = members; - } - - /** - * Adds a Widget. - * - * @param widget the widget - * @return the boolean - */ - @Override - public boolean addWidget(Widget widget) { - if (widget.memberOf(members)) { - if (vserver == null && widget.getId().equals(new VServerWidget().getId())) { - addVserverWidget(widget); - } else if (widget.getId().equals(new LIntfWidget().getId())) { - return addLIntfWidget(widget); - } else if (widget.getId().equals(new VolumeWidget().getId())) { - addVolumeWidget(widget); - return true; - } - if (widget.getId().equals(new OamNetwork().getId())) { - return false; - } - return widgets.add(widget); - } - return false; - } - - private void addVolumeWidget(Widget widget) { - if (vserver != null) { - vserver.addWidget(widget); - } else { - addvolume = true; - } - } - - /** - * @param widget - * @return - */ - private boolean addLIntfWidget(Widget widget) { - if (vserver != null) { - vserver.addWidget(widget); - return true; - } else { - addlintf = true; - return false; - } - } - - private void addVserverWidget(Widget widget) { - vserver = widget; - if (addlintf) { - vserver.addWidget(new LIntfWidget()); - } - if (addvolume) { - vserver.addWidget(new VolumeWidget()); - } - } - - @Override - public int hashCode() { - return getModelNameVersionId().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Resource) { - return getModelNameVersionId().equals(((Resource) obj).getModelNameVersionId()); - } - return false; - } -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java b/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java deleted file mode 100644 index be75d65..0000000 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/VirtualFunction.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ============LICENSE_START======================================================= - * org.onap.aai - * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ -package org.onap.aai.babel.xml.generator.model; - -import org.onap.aai.babel.xml.generator.types.Cardinality; -import org.onap.aai.babel.xml.generator.types.Model; - -@Model(widget = Widget.Type.VF, cardinality = Cardinality.UNBOUNDED, dataDeleteFlag = true) -public class VirtualFunction extends Resource { -} diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java index 963d9e2..11a9612 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashSet; +import java.util.Map; import java.util.Properties; import java.util.Set; import org.onap.aai.babel.logging.ApplicationMsgs; @@ -42,24 +43,7 @@ public abstract class Widget extends Model { "Cannot generate artifacts. Widget configuration not found for %s"; public enum Type { - SERVICE, - VF, - VFC, - VSERVER, - VOLUME, - FLAVOR, - TENANT, - VOLUME_GROUP, - LINT, - L3_NET, - VFMODULE, - IMAGE, - OAM_NETWORK, - ALLOTTED_RESOURCE, - TUNNEL_XCONNECT, - CONFIGURATION, - CR, - INSTANCE_GROUP; + SERVICE, VF, VFC, VSERVER, VOLUME, FLAVOR, TENANT, VOLUME_GROUP, LINT, L3_NET, VFMODULE, IMAGE, OAM_NETWORK, ALLOTTED_RESOURCE, TUNNEL_XCONNECT, CONFIGURATION, CR, INSTANCE_GROUP; } private static Logger log = LogHelper.INSTANCE; @@ -91,7 +75,8 @@ public abstract class Widget extends Model { /** * Gets widget. * - * @param type the type + * @param type + * the type * @return the widget */ public static Widget getWidget(Type type) { @@ -108,8 +93,14 @@ public abstract class Widget extends Model { return widget; } + @Override + public boolean isResource() { + return false; + } + public String getId() { - String id = WidgetConfigurationUtil.getConfig().getProperty(ArtifactType.AAI.name() + ".model-version-id." + getName()); + String id = WidgetConfigurationUtil.getConfig() + .getProperty(ArtifactType.AAI.name() + ".model-version-id." + getName()); if (id == null) { throw new IllegalArgumentException(String.format(GENERATOR_AAI_CONFIGLPROP_NOT_FOUND, ArtifactType.AAI.name() + ".model-version-id." + getName())); @@ -156,7 +147,8 @@ public abstract class Widget extends Model { /** * Equals method that compares Widget IDs. * - * @param obj the Widget object to compare + * @param obj + * the Widget object to compare * @return whether or not obj is equal to this Widget */ @Override @@ -179,7 +171,8 @@ public abstract class Widget extends Model { /** * Determine whether one or more keys belonging to this Widget appear in the specified Collection. * - * @param keys the keys + * @param keys + * the keys * @return the boolean */ public boolean memberOf(Collection keys) { @@ -198,4 +191,9 @@ public abstract class Widget extends Model { public boolean addWidget(Widget widget) { return true; } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } } diff --git a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java index 52dd462..d7519c0 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestArtifactGeneratorToscaParser.java @@ -32,9 +32,8 @@ import java.util.List; import org.junit.Test; import org.mockito.Mockito; 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.Resource; +import org.onap.aai.babel.xml.generator.model.Widget.Type; import org.onap.sdc.tosca.parser.api.ISdcCsarHelper; import org.onap.sdc.toscaparser.api.Group; import org.onap.sdc.toscaparser.api.NodeTemplate; @@ -55,7 +54,8 @@ public class TestArtifactGeneratorToscaParser { @Test(expected = IllegalArgumentException.class) public void testMissingProvidingService() { List nodeTemplateList = Collections.singletonList(buildNodeTemplate("name", "BlockStorage")); - new ArtifactGeneratorToscaParser(null).processResourceModels(new AllotedResource(), nodeTemplateList); + new ArtifactGeneratorToscaParser(null).processResourceModels(new Resource(Type.ALLOTTED_RESOURCE, true), + nodeTemplateList); } /** @@ -65,7 +65,8 @@ public class TestArtifactGeneratorToscaParser { @Test(expected = IllegalArgumentException.class) public void testAddResourceNotProvidingService() { List nodeTemplateList = Collections.singletonList(buildNodeTemplate("testCR", "CR")); - final Resource dummyResource = new AllotedResource(); // Any Resource to which the CR can be added + // Create any Resource to which the CR can be added + final Resource dummyResource = new Resource(Type.ALLOTTED_RESOURCE, true); new ArtifactGeneratorToscaParser(null).processResourceModels(dummyResource, nodeTemplateList); } @@ -90,7 +91,8 @@ public class TestArtifactGeneratorToscaParser { Mockito.when(helper.getGroupsOfOriginOfNodeTemplate(serviceNodeTemplate)).thenReturn(groups); ArtifactGeneratorToscaParser parser = new ArtifactGeneratorToscaParser(helper); - List resources = parser.processInstanceGroups(new InstanceGroup(), serviceNodeTemplate); + List resources = + parser.processInstanceGroups(new Resource(Type.INSTANCE_GROUP, true), serviceNodeTemplate); assertThat(resources.size(), is(1)); Resource resource = resources.get(0); @@ -102,9 +104,9 @@ public class TestArtifactGeneratorToscaParser { * sdc-tosca parser. * * @param name - * name of the NodeTemplate + * name of the NodeTemplate * @param type - * type of the NodeTemplate + * type of the NodeTemplate * @return a new NodeTemplate object */ private NodeTemplate buildNodeTemplate(String name, String type) { diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java index 912a505..3d5e841 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestModel.java @@ -21,14 +21,15 @@ package org.onap.aai.babel.xml.generator.model; -import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.Before; import org.junit.Test; import org.onap.aai.babel.parser.ArtifactGeneratorToscaParser; @@ -43,7 +44,8 @@ import org.onap.aai.babel.xml.generator.types.ModelType; public class TestModel { private Service serviceModel = new Service(); - private List resourceModels = Arrays.asList(new VirtualFunction(), new InstanceGroup()); + private List resourceModels = + Arrays.asList(new Resource(Type.CR, true), new Resource(Type.INSTANCE_GROUP, true)); private Widget widgetModel = new OamNetwork(); private Model anonymousModel; @@ -56,7 +58,7 @@ public class TestModel { * from the Artifact Generator properties. * * @throws IOException - * if the Artifact Generator properties file is not loaded + * if the Artifact Generator properties file is not loaded */ @Before public void setup() throws IOException { @@ -81,6 +83,16 @@ public class TestModel { public Type getWidgetType() { return null; } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + + @Override + public boolean isResource() { + return false; + } }; } @@ -90,34 +102,47 @@ public class TestModel { assertThat(Model.getModelFor(""), is(nullValue())); assertThat(Model.getModelFor("any.unknown.type"), is(nullValue())); - assertThat(Model.getModelFor("org.openecomp.resource.vf.allottedResource"), instanceOf(AllotedResource.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vf.allottedResource.with.sub.type"), - instanceOf(AllotedResource.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vfc.AllottedResource"), - instanceOf(ProvidingService.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vfc"), instanceOf(VServerWidget.class)); - assertThat(Model.getModelFor("org.openecomp.resource.cp"), instanceOf(LIntfWidget.class)); - assertThat(Model.getModelFor("org.openecomp.cp"), instanceOf(LIntfWidget.class)); - assertThat(Model.getModelFor("org.openecomp.cp.some.suffix"), instanceOf(LIntfWidget.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vl"), instanceOf(L3Network.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vf"), instanceOf(VirtualFunction.class)); - assertThat(Model.getModelFor("org.openecomp.groups.vfmodule"), instanceOf(VfModule.class)); - assertThat(Model.getModelFor("org.openecomp.groups.VfModule"), instanceOf(VfModule.class)); - assertThat(Model.getModelFor("org.openecomp.resource.vfc.nodes.heat.cinder"), instanceOf(VolumeWidget.class)); - assertThat(Model.getModelFor("org.openecomp.nodes.PortMirroringConfiguration"), - instanceOf(Configuration.class)); - assertThat(Model.getModelFor("org.openecomp.nodes.PortMirroringConfiguration", "Configuration"), - instanceOf(Configuration.class)); - assertThat(Model.getModelFor("any.string", "Configuration"), instanceOf(Configuration.class)); - assertThat(Model.getModelFor("org.openecomp.resource.cr.Kk1806Cr1", "CR"), instanceOf(CR.class)); - assertThat(Model.getModelFor("any.string", "CR"), instanceOf(CR.class)); - - assertThat(Model.getModelFor("org.openecomp.resource.vfc", "an.unknown.type"), instanceOf(VServerWidget.class)); + assertMapping("org.openecomp.resource.vfc", Type.VSERVER); + assertMapping("org.openecomp.resource.cp", Type.LINT); + assertMapping("org.openecomp.cp", Type.LINT); + assertMapping("org.openecomp.cp.some.suffix", Type.LINT); + assertMapping("org.openecomp.resource.vl", Type.L3_NET); + assertMapping("org.openecomp.resource.vf", Type.VF); + assertMapping("org.openecomp.groups.vfmodule", Type.VFMODULE); + assertMapping("org.openecomp.groups.VfModule", Type.VFMODULE); + assertMapping("org.openecomp.resource.vfc.nodes.heat.cinder", Type.VOLUME); + assertMapping("org.openecomp.nodes.PortMirroringConfiguration", "Configuration", Type.CONFIGURATION); + assertMapping("any.string", "Configuration", Type.CONFIGURATION); + assertMapping("org.openecomp.resource.cr.Kk1806Cr1", "CR", Type.CR); + assertMapping("any.string", "CR", Type.CR); + + assertMapping("org.openecomp.resource.vfc", "an.unknown.type", Type.VSERVER); } - @Test - public void testGetCardinality() { - resourceModels.get(0).getCardinality(); + /** + * Assert that the TOSCA type String is mapped to the expected Widget Type. + * + * @param toscaType + * the TOSCA type or prefix + * @param widgetType + * the type of Widget expected from the mappings + */ + private void assertMapping(String toscaType, Type widgetType) { + assertThat(Model.getModelFor(toscaType).getWidgetType(), is(widgetType)); + } + + /** + * Assert that the TOSCA metadata type is mapped to the expected Widget Type. + * + * @param toscaType + * the name (or name prefix) of the TOSCA type + * @param metadataType + * the type specified in the TOSCA metadata + * @param widgetType + * the type of Widget expected from the mappings + */ + private void assertMapping(String toscaType, String metadataType, Type widgetType) { + assertThat(Model.getModelFor(toscaType, metadataType).getWidgetType(), is(widgetType)); } @Test diff --git a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java index 1a5986b..451d2bb 100644 --- a/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java +++ b/src/test/java/org/onap/aai/babel/xml/generator/model/TestVfModule.java @@ -54,25 +54,25 @@ public class TestVfModule { */ @Test public void testHashCode() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); populateIdentInfo(vfModule); assertThat(vfModule.hashCode(), is(notNullValue())); } - + /** * Call equals() method for code coverage. */ @Test public void testEquals() { - VfModule vfModuleA = createNewVfModule(); - populateIdentInfo(vfModuleA); + Resource vfModuleA = createNewVfModule(); + populateIdentInfo(vfModuleA); - // equals() is reflexive + // equals() is reflexive assertThat(vfModuleA.equals(vfModuleA), is(true)); - + // equals() is symmetric - VfModule vfModuleB = createNewVfModule(); - populateIdentInfo(vfModuleB); + Resource vfModuleB = createNewVfModule(); + populateIdentInfo(vfModuleB); assertThat(vfModuleA.equals(vfModuleB), is(true)); assertThat(vfModuleB.equals(vfModuleA), is(true)); @@ -95,7 +95,7 @@ public class TestVfModule { */ @Test public void testNonMemberWidgetToVf() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); assertThat(vfModule.addWidget(createNewWidget(Type.SERVICE)), is(false)); assertNumberOfWidgets(vfModule, 0); } @@ -105,7 +105,7 @@ public class TestVfModule { */ @Test public void testAddOamNetworkWidgetToVf() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); assertThat(createNewWidgetForModule(vfModule, Type.OAM_NETWORK), is(false)); assertNumberOfWidgets(vfModule, 0); } @@ -120,7 +120,7 @@ public class TestVfModule { */ @Test public void testAddVolumeWidgetToVf() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); // Adding a Volume widget has no effect until a vserver widget is added. assertAddWidget(vfModule, Type.VOLUME); @@ -152,7 +152,7 @@ public class TestVfModule { */ @Test public void testAddLinterfaceWidgetToVf() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); // Adding an L-Interface widget has no effect until a vserver widget is added. assertFailToAddWidget(vfModule, Type.LINT); @@ -185,7 +185,7 @@ public class TestVfModule { */ @Test public void testAddVolumeAndLinterfaceWidgetToVf() { - VfModule vfModule = createNewVfModule(); + Resource vfModule = createNewVfModule(); // Adding a Volume widget has no effect until a vserver widget is added. assertAddWidget(vfModule, Type.VOLUME); @@ -232,8 +232,8 @@ public class TestVfModule { * * @return new VF Module resource */ - private VfModule createNewVfModule() { - VfModule vfModule = new VfModule(); + private Resource createNewVfModule() { + Resource vfModule = new Resource(Type.VFMODULE, true); assertNumberOfWidgets(vfModule, 0); return vfModule; } @@ -244,7 +244,7 @@ public class TestVfModule { * @param vfModule * to be populated */ - private void populateIdentInfo(VfModule vfModule) { + private void populateIdentInfo(Resource vfModule) { Map modelIdentInfo = new HashMap<>(); modelIdentInfo.put("UUID", "dummy_uuid"); vfModule.populateModelIdentificationInformation(modelIdentInfo); @@ -258,7 +258,7 @@ public class TestVfModule { * @param widgetType * the type of Widget to create and add */ - private void assertAddWidget(VfModule vfModule, Type widgetType) { + private void assertAddWidget(Resource vfModule, Type widgetType) { assertThat(createNewWidgetForModule(vfModule, widgetType), is(true)); } @@ -270,7 +270,7 @@ public class TestVfModule { * @param widgetType * the type of Widget to create and attempt to add */ - private void assertFailToAddWidget(VfModule vfModule, Type widgetType) { + private void assertFailToAddWidget(Resource vfModule, Type widgetType) { assertThat(createNewWidgetForModule(vfModule, widgetType), is(false)); } @@ -283,7 +283,7 @@ public class TestVfModule { * the type of Widget to create and attempt to add * @return whether or not the Widget was added to the module */ - private boolean createNewWidgetForModule(VfModule vfModule, Type widgetType) { + private boolean createNewWidgetForModule(Resource vfModule, Type widgetType) { Widget widget = createNewWidget(widgetType); setWidgetAsMember(vfModule, widget); return vfModule.addWidget(widget); @@ -299,7 +299,7 @@ public class TestVfModule { * @param widget * the widget to be set as the member */ - private void setWidgetAsMember(VfModule vfModule, Widget widget) { + private void setWidgetAsMember(Resource vfModule, Widget widget) { String id = widget.getId(); widget.addKey(id); vfModule.setMembers(Collections.singletonList(id)); @@ -312,7 +312,7 @@ public class TestVfModule { * the VF Module to update * @return the number of Widgets present in the vserver on creation */ - private int createVserverForVf(VfModule vfModule) { + private int createVserverForVf(Resource vfModule) { VServerWidget vserverWidget = (VServerWidget) createNewWidget(Type.VSERVER); assertNumberOfWidgets(vfModule, 0); final int initialWidgetCount = addVserverToVf(vfModule, vserverWidget); @@ -329,7 +329,7 @@ public class TestVfModule { * the Widget to add * @return initial widget count for the vserver Widget */ - private int addVserverToVf(VfModule vfModule, VServerWidget vserverWidget) { + private int addVserverToVf(Resource vfModule, VServerWidget vserverWidget) { // A vserver (initially) has Flavor, Image, Tenant and Vfc. final int initialWidgetCount = 4; assertNumberOfWidgets(vserverWidget, initialWidgetCount); diff --git a/src/test/resources/tosca-mappings.json b/src/test/resources/tosca-mappings.json index 9c3d0b4..0d306ea 100644 --- a/src/test/resources/tosca-mappings.json +++ b/src/test/resources/tosca-mappings.json @@ -1,21 +1,53 @@ { - "instanceGroupTypes": [ - "org.openecomp.groups.NetworkCollection", - "org.openecomp.groups.VfcInstanceGroup", - "org.openecomp.groups.ResourceInstanceGroup" - ], - "toscaToWidgetMappings": { - "org.openecomp.resource.vf.allottedResource": "AllotedResource", - "org.openecomp.resource.vfc.AllottedResource": "ProvidingService", - "org.openecomp.resource.vfc": "VServerWidget", - "org.openecomp.resource.cp": "LIntfWidget", - "org.openecomp.cp": "LIntfWidget", - "org.openecomp.resource.vl": "L3Network", - "org.openecomp.resource.vf": "VirtualFunction", - "org.openecomp.groups.vfmodule": "VfModule", - "org.openecomp.groups.VfModule": "VfModule", - "org.openecomp.resource.vfc.nodes.heat.cinder": "VolumeWidget", - "org.openecomp.nodes.PortMirroringConfiguration": "Configuration", - "org.openecomp.resource.cr.Kk1806Cr1": "CR" - } + "instanceGroupTypes": [ + "org.openecomp.groups.NetworkCollection", + "org.openecomp.groups.VfcInstanceGroup", + "org.openecomp.groups.ResourceInstanceGroup" + ], + "widgetMappings": [ + { + "prefix": "org.openecomp.resource.vfc", + "type": "widget", + "widget": "VSERVER", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.resource.cp", + "type": "widget", + "widget": "LINT", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.cp", + "type": "widget", + "widget": "LINT", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.resource.vl", + "widget": "L3_NET", + "deleteFlag": false + }, + { + "prefix": "org.openecomp.resource.vf", + "widget": "VF", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.groups.vfmodule", + "widget": "VFMODULE", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.groups.VfModule", + "widget": "VFMODULE", + "deleteFlag": true + }, + { + "prefix": "org.openecomp.resource.vfc.nodes.heat.cinder", + "type": "widget", + "widget": "VOLUME", + "deleteFlag": true + } + ] } -- 2.16.6 From 4217e6f7018d08b11291490b3ad5c54064cdc031 Mon Sep 17 00:00:00 2001 From: "mark.j.leonard" Date: Tue, 5 Feb 2019 15:47:47 +0000 Subject: [PATCH 16/16] Fix checkstyle issues including javadoc Minor code tidy-ups: mostly formatting and Javadoc comments Change-Id: I8a9ab692428fa09c73bd1fb542100610a86f6eca Issue-ID: AAI-2121 Signed-off-by: mark.j.leonard --- .../org/onap/aai/auth/AAIMicroServiceAuth.java | 6 ++-- .../onap/aai/babel/logging/ApplicationMsgs.java | 29 ++++++++-------- .../aai/babel/xml/generator/ModelGenerator.java | 21 ++++++++---- .../xml/generator/api/AaiModelGeneratorImpl.java | 38 +++++++++++--------- .../aai/babel/xml/generator/model/Resource.java | 14 ++++---- .../onap/aai/babel/xml/generator/model/Widget.java | 3 +- .../org/onap/aai/babel/MicroServiceAuthTest.java | 40 +++++++++++++++------- .../aai/babel/csar/vnfcatalog/SdcToscaHelper.java | 14 +++++--- .../org/onap/aai/babel/parser/TestToscaParser.java | 5 +-- .../aai/babel/service/CsarToXmlConverterTest.java | 18 ++++++---- .../service/TestGenerateArtifactsServiceImpl.java | 10 ++++-- .../java/org/onap/aai/babel/testdata/CsarTest.java | 22 ++++++++---- .../org/onap/aai/babel/util/ArtifactTestUtils.java | 12 +++++++ 13 files changed, 148 insertions(+), 84 deletions(-) diff --git a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java index c2402cd..5e4f3c4 100644 --- a/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java +++ b/src/main/java/org/onap/aai/auth/AAIMicroServiceAuth.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.auth; import java.security.cert.X509Certificate; @@ -41,6 +42,7 @@ public class AAIMicroServiceAuth { /** * @param babelAuthConfig * @throws AAIAuthException + * if the Auth Policy cannot be loaded */ @Inject public AAIMicroServiceAuth(final BabelAuthConfig babelAuthConfig) throws AAIAuthException { diff --git a/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java b/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java index 7178810..7d18a40 100644 --- a/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java +++ b/src/main/java/org/onap/aai/babel/logging/ApplicationMsgs.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.logging; import com.att.eelf.i18n.EELFResourceManager; @@ -25,18 +26,18 @@ import org.onap.aai.cl.eelf.LogMessageEnum; public enum ApplicationMsgs implements LogMessageEnum { - DISTRIBUTION_EVENT, - MESSAGE_AUDIT, - MESSAGE_METRIC, - MISSING_REQUEST_ID, - PROCESS_REQUEST_ERROR, - INVALID_CSAR_FILE, - INVALID_REQUEST_JSON, - BABEL_REQUEST_PAYLOAD, - BABEL_RESPONSE_PAYLOAD, - LOAD_PROPERTIES, - PROCESSING_VNF_CATALOG_ERROR, - TEMP_FILE_ERROR, + DISTRIBUTION_EVENT, // + MESSAGE_AUDIT, // + MESSAGE_METRIC, // + MISSING_REQUEST_ID, // + PROCESS_REQUEST_ERROR, // + INVALID_CSAR_FILE, // + INVALID_REQUEST_JSON, // + BABEL_REQUEST_PAYLOAD, // + BABEL_RESPONSE_PAYLOAD, // + LOAD_PROPERTIES, // + PROCESSING_VNF_CATALOG_ERROR, // + TEMP_FILE_ERROR, // MISSING_SERVICE_METADATA; static { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java b/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java index f474321..bbd1ff3 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/ModelGenerator.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.xml.generator; import java.util.Base64; @@ -42,6 +43,7 @@ import org.onap.aai.cl.api.Logger; */ public class ModelGenerator implements ArtifactGenerator { + private static final Logger logger = LogHelper.INSTANCE; private static final String VERSION_DELIMITER = "."; @@ -52,9 +54,11 @@ public class ModelGenerator implements ArtifactGenerator { * Invokes the TOSCA artifact generator API with the input artifacts. * * @param csarArchive - * @param csarArtifacts the input artifacts + * @param csarArtifacts + * the input artifacts * @return {@link List} of output artifacts - * @throws XmlArtifactGenerationException if there is an error trying to generate XML artifacts + * @throws XmlArtifactGenerationException + * if there is an error trying to generate XML artifacts */ @Override public List generateArtifacts(byte[] csarArchive, List csarArtifacts) @@ -89,9 +93,12 @@ public class ModelGenerator implements ArtifactGenerator { /** * Creates an instance of an input artifact for the generator. * - * @param payload the payload downloaded from SDC - * @param artifactName name of the artifact to create - * @param artifactVersion version of the artifact to create + * @param payload + * the payload downloaded from SDC + * @param artifactName + * name of the artifact to create + * @param artifactVersion + * version of the artifact to create * @return an {@link Artifact} object constructed from the payload and artifactInfo */ public static Artifact createArtifact(byte[] payload, String artifactName, String artifactVersion) { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java index 488faae..3bff7e7 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/api/AaiModelGeneratorImpl.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.xml.generator.api; import java.io.StringWriter; @@ -52,7 +53,8 @@ public class AaiModelGeneratorImpl implements AaiModelGenerator { /** * Method to generate the AAI model for a Service. * - * @param service Java object model representing an AAI {@link Service} model + * @param service + * Java object model representing an AAI {@link Service} model * @return XML representation of the service model in String format */ @Override @@ -111,7 +113,8 @@ public class AaiModelGeneratorImpl implements AaiModelGenerator { /** * Method to generate the AAI model for a Resource. * - * @param resource Java object model representing an AAI {@link Resource} model + * @param resource + * Java object model representing an AAI {@link Resource} model * @return XML representation of the resource model in String format */ @Override @@ -164,10 +167,11 @@ public class AaiModelGeneratorImpl implements AaiModelGenerator { /** * Method to create the holding the relationship value for a resource/widget model. * - * @param newDataDelFlag Value of the attribute for a widget/resource in the - * model xml - * @param relationshipValue Value of the attribute for the widget/resource - * in the model xml + * @param newDataDelFlag + * Value of the attribute for a widget/resource in the model xml + * @param relationshipValue + * Value of the attribute for the widget/resource in the model + * xml * @return Java object representation for the holding the relationship */ private ModelElement createRelationshipModelElement(String newDataDelFlag, String modelVersionId, @@ -200,8 +204,10 @@ public class AaiModelGeneratorImpl implements AaiModelGenerator { * Method to create the child model elements of the widget. Handles the generation of recursive child widget * elements (if any) * - * @param parent Reference to the parent widget model element - * @param widgetChildrenSet Set of children obtained from the tosca/widget definition + * @param parent + * Reference to the parent widget model element + * @param widgetChildrenSet + * Set of children obtained from the tosca/widget definition */ private void generateWidgetChildren(ModelElement parent, Set widgetChildrenSet) { for (Widget widget : widgetChildrenSet) { @@ -223,21 +229,19 @@ public class AaiModelGeneratorImpl implements AaiModelGenerator { /** * Converts the data delete flag value from boolean to String as per AAI model. * - * @param delFlag Boolean value as true/false from the annotation + * @param delFlag + * Boolean value as true/false from the annotation * @return Converted value to a flag as per AAI model */ private String getNewDataDelFlagValue(boolean delFlag) { - if (delFlag) { - return "T"; - } else { - return "F"; - } + return delFlag ? "T" : "F"; } /** * JAXB marshalling helper method to convert the Java object model to XML String. * - * @param model Java Object model of a service/widget/resource + * @param model + * Java Object model of a service/widget/resource * @return XML representation of the Java model in String format */ private String getModelAsString(Model model) { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java index 121bc19..dffff62 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Resource.java @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.xml.generator.model; import java.util.Collections; @@ -29,7 +30,7 @@ public class Resource extends Model { private Type type; private boolean deleteFlag; - private boolean isResource = true; + private boolean isResource = true; private Map properties = Collections.emptyMap(); Widget vserver = null; @@ -106,18 +107,17 @@ public class Resource extends Model { public boolean addWidget(Widget widget) { if (type == Type.VFMODULE) { if (widget.memberOf(members)) { - if (vserver == null && widget.getId().equals(new VServerWidget().getId())) { + if (vserver == null && widget instanceof VServerWidget) { addVserverWidget(widget); - } else if (widget.getId().equals(new LIntfWidget().getId())) { + } else if (widget instanceof LIntfWidget) { return addLIntfWidget(widget); - } else if (widget.getId().equals(new VolumeWidget().getId())) { + } else if (widget instanceof VolumeWidget) { addVolumeWidget(widget); return true; } - if (widget.getId().equals(new OamNetwork().getId())) { - return false; + if (!(widget instanceof OamNetwork)) { + return widgets.add(widget); } - return widgets.add(widget); } return false; } else { diff --git a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java index 11a9612..be84526 100644 --- a/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java +++ b/src/main/java/org/onap/aai/babel/xml/generator/model/Widget.java @@ -114,8 +114,7 @@ public abstract class Widget extends Model { } public String getName() { - ModelWidget widgetModel = this.getClass().getAnnotation(ModelWidget.class); - return widgetModel.name(); + return this.getClass().getAnnotation(ModelWidget.class).name(); } /** diff --git a/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java b/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java index a8f1f92..aae7846 100644 --- a/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java +++ b/src/test/java/org/onap/aai/babel/MicroServiceAuthTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel; import static org.hamcrest.CoreMatchers.is; @@ -37,7 +38,7 @@ import org.onap.aai.babel.config.BabelAuthConfig; import org.springframework.mock.web.MockHttpServletRequest; /** - * Tests @{link AAIMicroServiceAuth} + * Tests @{link AAIMicroServiceAuth}. */ public class MicroServiceAuthTest { @@ -54,10 +55,10 @@ public class MicroServiceAuthTest { * of a policy file that does not exist. * * @throws AAIAuthException - * @throws IOException + * if the Auth policy file cannot be loaded */ @Test(expected = AAIAuthException.class) - public void missingPolicyFile() throws AAIAuthException, IOException { + public void missingPolicyFile() throws AAIAuthException { String defaultFile = AAIMicroServiceAuthCore.getDefaultAuthFileName(); try { AAIMicroServiceAuthCore.setDefaultAuthFileName("invalid.default.file"); @@ -70,14 +71,17 @@ public class MicroServiceAuthTest { } /** - * Test loading of a temporary file created with the specified roles + * Test loading of a temporary file created with the specified roles. * * @throws AAIAuthException + * if the test creates invalid Auth Policy roles * @throws IOException + * for I/O failures * @throws JSONException + * if this test creates an invalid JSON object */ @Test - public void createLocalAuthFile() throws AAIAuthException, IOException, JSONException { + public void createLocalAuthFile() throws JSONException, AAIAuthException, IOException { JSONObject roles = createRoleObject("role", createUserObject("user"), createFunctionObject("func")); createAuthService(roles); assertThat(AAIMicroServiceAuthCore.authorize("nosuchuser", "method:func"), is(false)); @@ -85,9 +89,10 @@ public class MicroServiceAuthTest { } /** - * Test that the default policy file is loaded when a non-existent file is passed to the authorisation clas. + * Test that the default policy file is loaded when a non-existent file is passed to the authorisation class. * * @throws AAIAuthException + * if the Auth Policy cannot be loaded */ @Test public void createAuthFromDefaultFile() throws AAIAuthException { @@ -99,9 +104,10 @@ public class MicroServiceAuthTest { } /** - * Test loading of the policy file relative to CONFIG_HOME + * Test loading of the policy file relative to CONFIG_HOME. * * @throws AAIAuthException + * if the Auth Policy cannot be loaded */ @Test public void createAuth() throws AAIAuthException { @@ -129,12 +135,17 @@ public class MicroServiceAuthTest { } /** - * @param rolesJson - * @return + * Create a test Auth policy JSON file and pass this to the Auth Service. + * + * @param roles + * the Auth policy JSON content + * @return a new Auth Service configured with the supplied roles * @throws IOException + * for I/O failures * @throws AAIAuthException + * if the auth policy file cannot be loaded */ - private AAIMicroServiceAuth createAuthService(JSONObject roles) throws IOException, AAIAuthException { + private AAIMicroServiceAuth createAuthService(JSONObject roles) throws AAIAuthException, IOException { File file = File.createTempFile("auth-policy", "json"); file.deleteOnExit(); FileWriter fileWriter = new FileWriter(file); @@ -148,11 +159,14 @@ public class MicroServiceAuthTest { } /** - * Assert authorisation results for an admin user based on the test policy file + * Assert authorisation results for an admin user based on the test policy file. * * @param auth + * the Auth Service to test * @param adminUser + * admin username * @throws AAIAuthException + * if the Auth Service is not initialized */ private void assertAdminUserAuthorisation(AAIMicroServiceAuth auth, String adminUser) throws AAIAuthException { assertThat(AAIMicroServiceAuthCore.authorize(adminUser, "GET:actions"), is(true)); diff --git a/src/test/java/org/onap/aai/babel/csar/vnfcatalog/SdcToscaHelper.java b/src/test/java/org/onap/aai/babel/csar/vnfcatalog/SdcToscaHelper.java index 9fcc5a4..6fbb1f1 100644 --- a/src/test/java/org/onap/aai/babel/csar/vnfcatalog/SdcToscaHelper.java +++ b/src/test/java/org/onap/aai/babel/csar/vnfcatalog/SdcToscaHelper.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,9 @@ public class SdcToscaHelper { private ArrayList smnodetemplates = new ArrayList<>(); /** - * @return + * Create the test SubstitutionMappings. + * + * @return the new Substitution Mappings */ public SubstitutionMappings buildMappings() { LinkedHashMap defProps = getImagesDefProps(); @@ -78,7 +80,7 @@ public class SdcToscaHelper { } /** - * + * Create a new NodeTemplate and add it to the list (for populating the Substitution Mappings). */ public void addNodeTemplate() { String name = "node name"; @@ -98,7 +100,11 @@ public class SdcToscaHelper { } /** + * Simulate the creation of a NodeTemplate by the SDC TOSCA parser. Populate the properties of the NodeTemplate with + * the supplied images. + * * @param images + * the value of the images property */ public void addNodeTemplate(Object images) { LinkedHashMap properties = new LinkedHashMap<>(); diff --git a/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java index f8d8478..348e1a1 100644 --- a/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java +++ b/src/test/java/org/onap/aai/babel/parser/TestToscaParser.java @@ -18,6 +18,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ + package org.onap.aai.babel.parser; import static org.hamcrest.CoreMatchers.equalTo; @@ -64,8 +65,8 @@ public class TestToscaParser { GenerationData data = generator.generateArtifact(CsarTest.VNF_VENDOR_CSAR.getContent(), ymlFiles, additionalParams); - assertThat(data.getErrorData().size(), is(equalTo(0))); - assertThat(data.getResultData().size(), is(equalTo(2))); + assertThat("Number of errors produced", data.getErrorData().size(), is(equalTo(0))); + assertThat("Number of resources generated", data.getResultData().size(), is(equalTo(2))); } } diff --git a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java index 1ead8e6..bcd1e8a 100644 --- a/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java +++ b/src/test/java/org/onap/aai/babel/service/CsarToXmlConverterTest.java @@ -113,9 +113,6 @@ public class CsarToXmlConverterTest { * if there is an error either extracting the YAML files or generating XML artifacts * @throws IOException * if an I/O exception occurs loading the test CSAR file - * @throws IOException - * @throws XmlArtifactGenerationException - * @throws CsarConverterException */ @Test public void testArtifactGeneratorConfigMissing() throws CsarConverterException, IOException { @@ -131,13 +128,13 @@ public class CsarToXmlConverterTest { /** * Test that an Exception is thrown when the Artifact Generator's Group Filter properties are not present. * - * @throws IOException - * @throws XmlArtifactGenerationException * @throws CsarConverterException + * if there is an error either extracting the YAML files or generating XML artifacts + * @throws IOException + * if an I/O exception occurs */ @Test - public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() - throws IOException, XmlArtifactGenerationException, CsarConverterException { + public void generateXmlFromCsarFilterTypesSystemPropertyNotSet() throws CsarConverterException, IOException { exception.expect(CsarConverterException.class); exception.expectMessage("Cannot generate artifacts. System property " + ArtifactGeneratorToscaParser.PROPERTY_GROUP_FILTERS_CONFIG_FILE + " not configured"); @@ -195,6 +192,13 @@ public class CsarToXmlConverterTest { assertThatGeneratedFilesMatchExpected(createExpectedXmlFiles(filesToLoad), CsarTest.SERVICE_PROXY_CSAR_FILE); } + /** + * A Matcher for comparing generated XML Strings with expected XML content. + * + * @param expected + * the expected XML String + * @return a new Matcher for comparing XML Strings + */ public Matcher matches(final String expected) { return new BaseMatcher() { protected String theExpected = expected; diff --git a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java index d87f3c3..a38686f 100644 --- a/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java +++ b/src/test/java/org/onap/aai/babel/service/TestGenerateArtifactsServiceImpl.java @@ -84,11 +84,14 @@ public class TestGenerateArtifactsServiceImpl { /** * No VNF Configuration exists. - * - * @throws Exception + * + * @throws URISyntaxException + * if the URI cannot be created + * @throws IOException + * if the resource cannot be loaded */ @Test - public void testGenerateArtifactsWithoutVnfConfiguration() throws Exception { + public void testGenerateArtifactsWithoutVnfConfiguration() throws IOException, URISyntaxException { Response response = processJsonRequest(CsarTest.NO_VNF_CONFIG_CSAR); assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); assertThat(response.getEntity(), is(getResponseJson("validNoVnfConfigurationResponse.json"))); @@ -146,6 +149,7 @@ public class TestGenerateArtifactsServiceImpl { * Create a (mocked) HTTPS request and invoke the Babel generate artifacts API. * * @param csar + * test CSAR file * @return the Response from the HTTP API * @throws URISyntaxException * if the URI cannot be created diff --git a/src/test/java/org/onap/aai/babel/testdata/CsarTest.java b/src/test/java/org/onap/aai/babel/testdata/CsarTest.java index 4f4c8ad..8b28551 100644 --- a/src/test/java/org/onap/aai/babel/testdata/CsarTest.java +++ b/src/test/java/org/onap/aai/babel/testdata/CsarTest.java @@ -2,8 +2,8 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. - * Copyright © 2017-2018 European Software Marketing Ltd. + * Copyright © 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017-2019 European Software Marketing Ltd. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,8 +66,10 @@ public enum CsarTest { * Extract YAML Artifacts. * * @return the extracted artifacts - * @throws InvalidArchiveException if the CSAR is invalid - * @throws IOException for I/O errors + * @throws InvalidArchiveException + * if the CSAR is invalid + * @throws IOException + * for I/O errors */ public List extractArtifacts() throws InvalidArchiveException, IOException { return new YamlExtractor().extract(getContent(), getName(), "v1"); @@ -77,14 +79,22 @@ public enum CsarTest { * Extract VNF Vendor Image Artifacts. * * @return the extracted artifacts - * @throws IOException * @throws ToscaToCatalogException - * + * if the CSAR content is not valid + * @throws IOException + * if an I/O exception occursSince: */ public BabelArtifact extractVnfVendorImages() throws ToscaToCatalogException, IOException { return new VnfVendorImageExtractor().extract(getContent()); } + /** + * Create a BabelRequest containing the encoded CSAR content. + * + * @return a new Babel request for this CSAR + * @throws IOException + * if an I/O exception occurs + */ public String getJsonRequest() throws IOException { BabelRequest request = new BabelRequest(); request.setArtifactName(getName()); diff --git a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java index 6608c00..445dd84 100644 --- a/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java +++ b/src/test/java/org/onap/aai/babel/util/ArtifactTestUtils.java @@ -56,6 +56,9 @@ public class ArtifactTestUtils { private static final String JSON_RESPONSES_FOLDER = "response/"; private static final String CSAR_INPUTS_FOLDER = "compressedArtifacts/"; + /** + * Initialise System Properties for test configuration files. + */ public void setGeneratorSystemProperties() { System.setProperty(ArtifactGeneratorToscaParser.PROPERTY_ARTIFACT_GENERATOR_CONFIG_FILE, getResourcePath(Resources.ARTIFACT_GENERATOR_CONFIG)); @@ -143,6 +146,15 @@ public class ArtifactTestUtils { return Files.lines(Paths.get(getResource(resourceFile).toURI())).collect(Collectors.joining()); } + /** + * Create Properties from the content of the named resource (e.g. a file on the classpath). + * + * @param resourceName + * the resource name + * @return Properties loaded from the named resource + * @throws IOException + * if an error occurred when reading from the named resource + */ public Properties getResourceAsProperties(String resourceName) throws IOException { final Properties properties = new Properties(); InputStream in = ArtifactTestUtils.class.getClassLoader().getResourceAsStream(resourceName); -- 2.16.6