# Introduction
The A&AI Model Loader Service is an application that facilitates the distribution and ingestion of
-new service and resource models from the SDC to the A&AI.
+new service and resource models and VNF catalogs from the SDC to the A&AI.
## Features
* polls the UEB/DMaap cluster for notification events
* downloads artifacts from SDC upon receipt of a distribution event
* pushes distribution components to A&AI
+
+### VNF Catalog loading
+The Model Loader supports two methods for supplying VNF Catalog data for loading into A&AI:
+
+* Embedded TOSCA image and vendor data<br/>VNF Catalog data can be embedded within the TOSCA yaml files contained in the CSAR.
+
+
+* VNF Catalog XML files<br/>VNF Catalog data in the form of XML files can be supplied in the CSAR under the path `Artifacts/Deployment/VNF_CATALOG`
+
+**Note: Each CSAR should provide VNF Catalog information using only one of the above methods. If a CSAR contains both TOSCA and XML VNF Catalog information, a deploy failure will be logged and published to SDC, and no VNF Catalog data will be loaded into A&AI**
+
## Compiling Model Loader
Model Loader can be compiled by running `mvn clean install`
The following file must be present in this directory on the host machine:
-_model-loader.properties_
+_model-loader.properties_
# Always false. TLS Auth currently not supported
ml.distribution.ACTIVE_SERVER_TLS_AUTH=false
{{your docker repo}}/model-loader
where
-
+
{{your docker repo}}
is the Docker repository you have published your image to.
<parent>
<groupId>org.onap.oparent</groupId>
<artifactId>oparent</artifactId>
- <version>1.1.0</version>
+ <version>1.1.1</version>
<relativePath />
</parent>
<docker.location>${basedir}/target</docker.location>
<spring-boot.version>1.5.12.RELEASE</spring-boot.version>
<apache.commons-text.version>1.1</apache.commons-text.version>
+ <commons-compress.version>1.14</commons-compress.version>
<hamcrest-all.version>1.3</hamcrest-all.version>
<babel.version>1.2.0</babel.version>
<aai.rest.client.version>1.2.1</aai.rest.client.version>
<artifactId>jline</artifactId>
<version>2.12.1</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>${commons-compress.version}</version>
+ </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
package org.onap.aai.modelloader.config;
import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
private static final String SUFFIX_TRUSTSTORE_PASS = "TRUSTSTORE_PASSWORD";
// Configuration file properties
- protected static final String PROP_ML_DISTRIBUTION_ACTIVE_SERVER_TLS_AUTH = PREFIX_DISTRIBUTION_CLIENT
- + "ACTIVE_SERVER_TLS_AUTH";
- protected static final String PROP_ML_DISTRIBUTION_ASDC_CONNECTION_DISABLED = PREFIX_DISTRIBUTION_CLIENT
- + "ASDC_CONNECTION_DISABLE";
+ protected static final String PROP_ML_DISTRIBUTION_ACTIVE_SERVER_TLS_AUTH =
+ PREFIX_DISTRIBUTION_CLIENT + "ACTIVE_SERVER_TLS_AUTH";
+ protected static final String PROP_ML_DISTRIBUTION_ASDC_CONNECTION_DISABLED =
+ PREFIX_DISTRIBUTION_CLIENT + "ASDC_CONNECTION_DISABLE";
protected static final String PROP_ML_DISTRIBUTION_ASDC_ADDRESS = PREFIX_DISTRIBUTION_CLIENT + "ASDC_ADDRESS";
protected static final String PROP_ML_DISTRIBUTION_CONSUMER_GROUP = PREFIX_DISTRIBUTION_CLIENT + "CONSUMER_GROUP";
protected static final String PROP_ML_DISTRIBUTION_CONSUMER_ID = PREFIX_DISTRIBUTION_CLIENT + "CONSUMER_ID";
- protected static final String PROP_ML_DISTRIBUTION_ENVIRONMENT_NAME = PREFIX_DISTRIBUTION_CLIENT
- + "ENVIRONMENT_NAME";
- protected static final String PROP_ML_DISTRIBUTION_KEYSTORE_PASSWORD = PREFIX_DISTRIBUTION_CLIENT
- + SUFFIX_KEYSTORE_PASS;
- protected static final String PROP_ML_DISTRIBUTION_KEYSTORE_FILE = PREFIX_DISTRIBUTION_CLIENT
- + SUFFIX_KEYSTORE_FILE;
+ protected static final String PROP_ML_DISTRIBUTION_ENVIRONMENT_NAME =
+ PREFIX_DISTRIBUTION_CLIENT + "ENVIRONMENT_NAME";
+ protected static final String PROP_ML_DISTRIBUTION_KEYSTORE_PASSWORD =
+ PREFIX_DISTRIBUTION_CLIENT + SUFFIX_KEYSTORE_PASS;
+ protected static final String PROP_ML_DISTRIBUTION_KEYSTORE_FILE =
+ PREFIX_DISTRIBUTION_CLIENT + SUFFIX_KEYSTORE_FILE;
protected static final String PROP_ML_DISTRIBUTION_PASSWORD = PREFIX_DISTRIBUTION_CLIENT + "PASSWORD";
- protected static final String PROP_ML_DISTRIBUTION_POLLING_INTERVAL = PREFIX_DISTRIBUTION_CLIENT
- + "POLLING_INTERVAL";
+ protected static final String PROP_ML_DISTRIBUTION_POLLING_INTERVAL =
+ PREFIX_DISTRIBUTION_CLIENT + "POLLING_INTERVAL";
protected static final String PROP_ML_DISTRIBUTION_POLLING_TIMEOUT = PREFIX_DISTRIBUTION_CLIENT + "POLLING_TIMEOUT";
protected static final String PROP_ML_DISTRIBUTION_USER = PREFIX_DISTRIBUTION_CLIENT + "USER";
protected static final String PROP_ML_DISTRIBUTION_ARTIFACT_TYPES = PREFIX_DISTRIBUTION_CLIENT + "ARTIFACT_TYPES";
- protected static final String PROP_ML_DISTRIBUTION_MSG_BUS_ADDRESSES = PREFIX_DISTRIBUTION_CLIENT
- + "MSG_BUS_ADDRESSES";
- protected static final String PROP_ML_DISTRIBUTION_HTTPS_WITH_DMAAP = PREFIX_DISTRIBUTION_CLIENT
- + "USE_HTTPS_WITH_DMAAP";
+ protected static final String PROP_ML_DISTRIBUTION_MSG_BUS_ADDRESSES =
+ PREFIX_DISTRIBUTION_CLIENT + "MSG_BUS_ADDRESSES";
+ protected static final String PROP_ML_DISTRIBUTION_HTTPS_WITH_DMAAP =
+ PREFIX_DISTRIBUTION_CLIENT + "USE_HTTPS_WITH_DMAAP";
protected static final String PROP_AAI_BASE_URL = PREFIX_AAI + "BASE_URL";
protected static final String PROP_AAI_KEYSTORE_FILE = PREFIX_AAI + SUFFIX_KEYSTORE_FILE;
protected static final String PROP_BABEL_GENERATE_RESOURCE_URL = PREFIX_BABEL + "GENERATE_ARTIFACTS_URL";
protected static final String PROP_DEBUG_INGEST_SIMULATOR = PREFIX_DEBUG + "INGEST_SIMULATOR";
- private static String configHome;
+ protected static final String FILESEP =
+ (System.getProperty("file.separator") == null) ? "/" : System.getProperty("file.separator");
+ private static String configHome;
private Properties modelLoaderProperties = null;
-
private String certLocation = ".";
-
private List<String> artifactTypes = null;
-
private List<String> msgBusAddrs = null;
-
private String modelVersion = null;
- protected static final String FILESEP = (System.getProperty("file.separator") == null) ? "/"
- : System.getProperty("file.separator");
-
- public static void setConfigHome(String configHome) {
- ModelLoaderConfig.configHome = configHome;
- }
-
- public static String propertiesFile() {
- return configHome + FILESEP + "model-loader.properties";
- }
-
public ModelLoaderConfig(Properties configProperties) {
this(configProperties, ModelLoaderConfig.configHome + FILESEP + "auth" + FILESEP);
}
/**
* Original constructor
*
- * @param modelLoaderProperties
- * properties needed to be configured for the model loader
- * @param certLocation
- * location of the certificate
+ * @param modelLoaderProperties properties needed to be configured for the model loader
+ * @param certLocation location of the certificate
*/
public ModelLoaderConfig(Properties modelLoaderProperties, String certLocation) {
this.modelLoaderProperties = modelLoaderProperties;
}
}
+ public static void setConfigHome(String configHome) {
+ ModelLoaderConfig.configHome = configHome;
+ }
+
+ public static Path propertiesFile() {
+ return Paths.get(configHome, "model-loader.properties");
+ }
+
@Override
public boolean activateServerTLSAuth() {
String value = modelLoaderProperties.getProperty(PROP_ML_DISTRIBUTION_ACTIVE_SERVER_TLS_AUTH);
MODEL,
MODEL_V8,
NAMED_QUERY,
- VNF_CATALOG;
+ VNF_CATALOG,
+ VNF_CATALOG_XML;
}
public VnfCatalogArtifact(String payload) {
this(ArtifactType.VNF_CATALOG, payload);
}
+
public VnfCatalogArtifact(ArtifactType artifactType, String payload) {
super(artifactType);
setPayload(payload);
/**\r
- * ============LICENSE_START=======================================================\r
+ * ============LICENSE_START=======================================================\r
* org.onap.aai\r
* ================================================================================\r
* Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.\r
\r
import com.google.gson.Gson;\r
import com.google.gson.reflect.TypeToken;\r
-import java.io.UnsupportedEncodingException;\r
+import java.io.StringReader;\r
+import java.net.URISyntaxException;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
import java.util.List;\r
import java.util.Map;\r
import java.util.Map.Entry;\r
import java.util.UUID;\r
import javax.ws.rs.core.MediaType;\r
import javax.ws.rs.core.Response;\r
+import javax.xml.parsers.DocumentBuilder;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
import org.apache.commons.text.StringEscapeUtils;\r
+import org.apache.http.client.utils.URIBuilder;\r
import org.onap.aai.cl.api.Logger;\r
import org.onap.aai.cl.eelf.LoggerFactory;\r
import org.onap.aai.modelloader.config.ModelLoaderConfig;\r
import org.onap.aai.modelloader.restclient.AaiRestClient;\r
import org.onap.aai.modelloader.service.ModelLoaderMsgs;\r
import org.onap.aai.restclient.client.OperationResult;\r
-import org.springframework.web.util.UriUtils;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.w3c.dom.Node;\r
+import org.w3c.dom.NodeList;\r
+import org.xml.sax.InputSource;\r
\r
/**\r
* VNF Catalog specific handling\r
return true;\r
}\r
\r
+ /*\r
+ * If something fails in the middle of ingesting the catalog we want to roll back any changes to the DB\r
+ */\r
+ @Override\r
+ public void rollback(List<Artifact> completedArtifacts, String distributionId, AaiRestClient aaiClient) {\r
+ for (Artifact completedArtifact : completedArtifacts) {\r
+ Map<String, String> data = new Gson().fromJson(completedArtifact.getPayload(),\r
+ new TypeToken<Map<String, String>>() {}.getType());\r
+ String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + data.get(ATTR_UUID);\r
+ // Try to delete the image. If something goes wrong we can't really do anything here\r
+ aaiClient.getAndDeleteResource(url, distributionId);\r
+ }\r
+ }\r
+\r
private void distributeVnfcData(AaiRestClient restClient, String distributionId, Artifact vnfcArtifact,\r
List<Artifact> completedArtifacts) throws VnfImageException {\r
+ List<Map<String, String>> vnfcData;\r
+ switch (vnfcArtifact.getType()) {\r
+ case VNF_CATALOG:\r
+ vnfcData = unmarshallVnfcData(vnfcArtifact);\r
+ break;\r
+ case VNF_CATALOG_XML:\r
+ vnfcData = parseXmlVnfcData(vnfcArtifact);\r
+ break;\r
+ default:\r
+ throw new VnfImageException("Unsupported type " + vnfcArtifact.getType());\r
+ }\r
+ distributeVnfcData(restClient, distributionId, completedArtifacts, vnfcData);\r
+ }\r
\r
- List<Map<String, String>> vnfcData = unmarshallVnfcData(vnfcArtifact);\r
-\r
+ /**\r
+ * Build a VNF image from each of the supplied data items, and distribute to AAI\r
+ * \r
+ * @param restClient\r
+ * @param distributionId\r
+ * @param completedArtifacts\r
+ * @param vnfcData\r
+ * @throws VnfImageException\r
+ */\r
+ private void distributeVnfcData(AaiRestClient restClient, String distributionId, List<Artifact> completedArtifacts,\r
+ List<Map<String, String>> vnfcData) throws VnfImageException {\r
for (Map<String, String> dataItem : vnfcData) {\r
// If an empty dataItem is supplied, do nothing.\r
if (dataItem.isEmpty()) {\r
continue;\r
}\r
\r
- String urlParams;\r
- StringBuilder imageId = new StringBuilder("vnf image");\r
-\r
- try {\r
- urlParams = buildUrlImgIdStrings(imageId, dataItem);\r
- } catch (UnsupportedEncodingException e) {\r
- throw new VnfImageException(e);\r
+ StringBuilder imageIdBuilder = new StringBuilder("vnf image");\r
+ for (Entry<String, String> entry : dataItem.entrySet()) {\r
+ imageIdBuilder.append(" ").append(entry.getValue());\r
}\r
+ String imageId = imageIdBuilder.toString();\r
+ int resultCode = getVnfImage(restClient, distributionId, imageId, dataItem);\r
\r
- OperationResult tryGet =\r
- restClient.getResource(config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "?" + urlParams,\r
- distributionId, MediaType.APPLICATION_JSON_TYPE);\r
-\r
- if (tryGet == null) {\r
- throw new VnfImageException(imageId.toString());\r
- }\r
-\r
- int resultCode = tryGet.getResultCode();\r
if (resultCode == Response.Status.NOT_FOUND.getStatusCode()) {\r
// This vnf-image is missing, so add it\r
boolean success = putVnfImage(restClient, dataItem, distributionId);\r
- if (!success) {\r
- throw new VnfImageException(imageId.toString());\r
+ if (success) {\r
+ completedArtifacts.add(new VnfCatalogArtifact(new Gson().toJson(dataItem)));\r
+ logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested.");\r
+ } else {\r
+ throw new VnfImageException(imageId);\r
}\r
- completedArtifacts.add(vnfcArtifact);\r
- logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested.");\r
} else if (resultCode == Response.Status.OK.getStatusCode()) {\r
logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " already exists. Skipping ingestion.");\r
} else {\r
// if other than 404 or 200, something went wrong\r
- throw new VnfImageException(imageId.toString(), resultCode);\r
+ throw new VnfImageException(imageId, resultCode);\r
}\r
}\r
}\r
\r
- private String buildUrlImgIdStrings(StringBuilder imageId, Map<String, String> dataItem)\r
- throws UnsupportedEncodingException {\r
- StringBuilder urlParams = new StringBuilder();\r
- for (Entry<String, String> entry : dataItem.entrySet()) {\r
- urlParams.append(entry.getKey()).append("=").append(UriUtils.encode(entry.getValue(), "UTF-8")).append("&");\r
- imageId.append(" ").append(entry.getValue());\r
+ private int getVnfImage(AaiRestClient restClient, String distributionId, String imageId,\r
+ Map<String, String> dataItem) throws VnfImageException {\r
+ try {\r
+ URIBuilder b = new URIBuilder(config.getAaiBaseUrl() + config.getAaiVnfImageUrl());\r
+ for (Entry<String, String> entry : dataItem.entrySet()) {\r
+ b.addParameter(entry.getKey(), entry.getValue());\r
+ }\r
+ OperationResult tryGet =\r
+ restClient.getResource(b.build().toString(), distributionId, MediaType.APPLICATION_JSON_TYPE);\r
+ if (tryGet == null) {\r
+ throw new VnfImageException(imageId);\r
+ }\r
+ return tryGet.getResultCode();\r
+ } catch (URISyntaxException ex) {\r
+ throw new VnfImageException(ex);\r
}\r
- return urlParams.deleteCharAt(urlParams.length() - 1).toString();\r
}\r
\r
private boolean putVnfImage(AaiRestClient restClient, Map<String, String> dataItem, String distributionId) {\r
new TypeToken<List<Map<String, String>>>() {}.getType());\r
}\r
\r
- /*\r
- * If something fails in the middle of ingesting the catalog we want to roll back any changes to the DB\r
+ /**\r
+ * Parse the VNF Catalog XML and transform into Key/Value pairs.\r
+ * \r
+ * @param vnfcArtifact\r
+ * @return VNF Image data in Map form\r
+ * @throws VnfImageException\r
*/\r
- @Override\r
- public void rollback(List<Artifact> completedArtifacts, String distributionId, AaiRestClient aaiClient) {\r
- for (Artifact completedArtifact : completedArtifacts) {\r
- List<Map<String, String>> completedImageData = unmarshallVnfcData(completedArtifact);\r
- for (Map<String, String> data : completedImageData) {\r
- String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + data.get(ATTR_UUID);\r
- // Try to delete the image. If something goes wrong we can't really do anything here\r
- aaiClient.getAndDeleteResource(url, distributionId);\r
+ private List<Map<String, String>> parseXmlVnfcData(Artifact vnfcArtifact) throws VnfImageException {\r
+ List<Map<String, String>> vnfcData = new ArrayList<>();\r
+ try {\r
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);\r
+ DocumentBuilder builder = factory.newDocumentBuilder();\r
+ InputSource is = new InputSource(new StringReader(vnfcArtifact.getPayload()));\r
+ Document doc = builder.parse(is);\r
+ doc.getDocumentElement().normalize();\r
+\r
+ NodeList pnl = doc.getElementsByTagName("part-number-list");\r
+ for (int i = 0; i < pnl.getLength(); i++) {\r
+ Node partNumber = pnl.item(i);\r
+ if (partNumber.getNodeType() == Node.ELEMENT_NODE) {\r
+ Element vendorInfo = getFirstChildNodeByName(partNumber, "vendor-info");\r
+ if (vendorInfo != null) {\r
+ Map<String, String> application = new HashMap<>();\r
+ application.put("application",\r
+ vendorInfo.getElementsByTagName("vendor-model").item(0).getTextContent());\r
+ application.put("application-vendor",\r
+ vendorInfo.getElementsByTagName("vendor-name").item(0).getTextContent());\r
+ populateSoftwareVersions(vnfcData, application, partNumber);\r
+ }\r
+ }\r
+ }\r
+ } catch (Exception ex) {\r
+ throw new VnfImageException(ex);\r
+ }\r
+ return vnfcData;\r
+ }\r
+\r
+ /**\r
+ * @param vnfcData to populate\r
+ * @param applicationData\r
+ * @param partNumber\r
+ */\r
+ private void populateSoftwareVersions(List<Map<String, String>> vnfcData, Map<String, String> applicationData,\r
+ Node partNumber) {\r
+ NodeList nodes = partNumber.getChildNodes();\r
+ for (int i = 0; i < nodes.getLength(); i++) {\r
+ Node childNode = nodes.item(i);\r
+ if (childNode.getNodeName().equalsIgnoreCase("software-version-list")) {\r
+ Element softwareVersion = getFirstChildNodeByName(childNode, "software-version");\r
+ if (softwareVersion != null) {\r
+ HashMap<String, String> vnfImageData = new HashMap<>(applicationData);\r
+ vnfImageData.put("application-version", softwareVersion.getTextContent());\r
+ vnfcData.add(vnfImageData);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param node\r
+ * @param childNodeName\r
+ * @return the first child node matching the given name\r
+ */\r
+ private Element getFirstChildNodeByName(Node node, String childNodeName) {\r
+ NodeList nodes = node.getChildNodes();\r
+ for (int i = 0; i < nodes.getLength(); i++) {\r
+ Node childNode = nodes.item(i);\r
+ if (childNode.getNodeName().equalsIgnoreCase(childNodeName)) {\r
+ return (Element) childNode;\r
}\r
}\r
+ return null;\r
}\r
\r
}\r
--- /dev/null
+/**
+ * ============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.modelloader.extraction;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.aai.cl.api.Logger;
+import org.onap.aai.cl.eelf.LoggerFactory;
+import org.onap.aai.modelloader.entity.Artifact;
+import org.onap.aai.modelloader.entity.ArtifactType;
+import org.onap.aai.modelloader.entity.catalog.VnfCatalogArtifact;
+import org.onap.aai.modelloader.service.ModelLoaderMsgs;
+
+
+/**
+ * The purpose of this class is to process a .csar file in the form of a byte array and extract VNF Catalog XML files
+ * from it.
+ *
+ * A .csar file is a compressed archive like a zip file and this class will treat the byte array as it if were a zip
+ * file.
+ */
+public class VnfCatalogExtractor {
+ private static final Logger logger = LoggerFactory.getInstance().getLogger(VnfCatalogExtractor.class.getName());
+
+ private static final Pattern VNFCFILE_EXTENSION_REGEX =
+ Pattern.compile("(?i)artifacts(\\\\|\\/)deployment(\\\\|\\/)vnf_catalog(\\\\|\\/).*\\.xml$");
+
+ /**
+ * This method is responsible for filtering the contents of the supplied archive and returning a collection of
+ * {@link Artifact}s that represent the VNF Catalog files that have been found in the archive.<br>
+ * <br>
+ * If the archive contains no VNF Catalog files it will return an empty list.<br>
+ *
+ * @param archive the zip file in the form of a byte array containing zero or more VNF Catalog files
+ * @param name the name of the archive file
+ * @return List<Artifact> collection of VNF Catalog XML files found in the archive
+ * @throws InvalidArchiveException if an error occurs trying to extract the VNFC files from the archive or if the
+ * archive is not a zip file
+ */
+ public List<Artifact> extract(byte[] archive, String name) throws InvalidArchiveException {
+ validateRequest(archive, name);
+
+ logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Extracting CSAR archive: " + name);
+
+ List<Artifact> vnfcFiles = new ArrayList<>();
+ try (SeekableInMemoryByteChannel inMemoryByteChannel = new SeekableInMemoryByteChannel(archive);
+ ZipFile zipFile = new ZipFile(inMemoryByteChannel)) {
+ for (Enumeration<ZipArchiveEntry> enumeration = zipFile.getEntries(); enumeration.hasMoreElements();) {
+ ZipArchiveEntry entry = enumeration.nextElement();
+ if (fileShouldBeExtracted(entry)) {
+ vnfcFiles.add(new VnfCatalogArtifact(ArtifactType.VNF_CATALOG_XML,
+ IOUtils.toString(zipFile.getInputStream(entry))));
+ }
+ }
+ } catch (IOException e) {
+ throw new InvalidArchiveException(
+ "An error occurred trying to create a ZipFile. Is the content being converted really a csar file?",
+ e);
+ }
+
+ logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, vnfcFiles.size() + " VNF Catalog files extracted.");
+
+ return vnfcFiles;
+ }
+
+ private static void validateRequest(byte[] archive, String name) throws InvalidArchiveException {
+ if (archive == null || archive.length == 0) {
+ throw new InvalidArchiveException("An archive must be supplied for processing.");
+ } else if (StringUtils.isBlank(name)) {
+ throw new InvalidArchiveException("The name of the archive must be supplied for processing.");
+ }
+ }
+
+ private static boolean fileShouldBeExtracted(ZipArchiveEntry entry) {
+ logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Checking if " + entry.getName() + " should be extracted...");
+ boolean extractFile = VNFCFILE_EXTENSION_REGEX.matcher(entry.getName()).matches();
+ logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Keeping file: " + entry.getName() + "? : " + extractFile);
+ return extractFile;
+ }
+}
+
/**
- * ============LICENSE_START=======================================================
+ * ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
* Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
import org.onap.aai.modelloader.entity.model.IModelParser;
import org.onap.aai.modelloader.entity.model.NamedQueryArtifactParser;
import org.onap.aai.modelloader.extraction.InvalidArchiveException;
+import org.onap.aai.modelloader.extraction.VnfCatalogExtractor;
import org.onap.aai.modelloader.restclient.BabelServiceClient;
import org.onap.aai.modelloader.restclient.BabelServiceClientException;
import org.onap.aai.modelloader.service.BabelServiceClientFactory;
private BabelArtifactConverter babelArtifactConverter;
private ModelLoaderConfig config;
private BabelServiceClientFactory clientFactory;
+ private VnfCatalogExtractor vnfCatalogExtractor;
public ArtifactDownloadManager(IDistributionClient client, ModelLoaderConfig config,
BabelServiceClientFactory clientFactory) {
}
public void processToscaArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts, byte[] payload,
+ IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
+ throws ProcessToscaArtifactsException, InvalidArchiveException {
+ // Get translated artifacts from Babel Service
+ invokeBabelService(modelArtifacts, catalogArtifacts, payload, artifactInfo, distributionId, serviceVersion);
+
+ // Get VNF Catalog artifacts directly from CSAR
+ List<Artifact> csarCatalogArtifacts = getVnfCatalogExtractor().extract(payload, artifactInfo.getArtifactName());
+
+ // Throw an error if VNF Catalog data is present in the Babel payload and directly in the CSAR
+ if (!catalogArtifacts.isEmpty() && !csarCatalogArtifacts.isEmpty()) {
+ logger.error(ModelLoaderMsgs.DUPLICATE_VNFC_DATA_ERROR, artifactInfo.getArtifactName());
+ throw new InvalidArchiveException("CSAR: " + artifactInfo.getArtifactName()
+ + " contains VNF Catalog data in the format of both TOSCA and XML files. Only one format can be used for each CSAR file.");
+ } else if (!csarCatalogArtifacts.isEmpty()) {
+ catalogArtifacts.addAll(csarCatalogArtifacts);
+ }
+ }
+
+ public void invokeBabelService(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts, byte[] payload,
IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
throws ProcessToscaArtifactsException {
try {
BabelServiceClient babelClient = createBabelServiceClient(artifactInfo, serviceVersion);
- logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT,
- "Posting artifact: " + artifactInfo.getArtifactName() + ", version: " + serviceVersion);
+ logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT,
+ "Posting artifact: " + artifactInfo.getArtifactName() + ", service version: " + serviceVersion
+ + ", artifact version: " + artifactInfo.getArtifactVersion());
List<BabelArtifact> babelArtifacts =
babelClient.postArtifact(payload, artifactInfo.getArtifactName(), serviceVersion, distributionId);
return babelArtifactConverter;
}
+
+ private VnfCatalogExtractor getVnfCatalogExtractor() {
+ if (vnfCatalogExtractor == null) {
+ vnfCatalogExtractor = new VnfCatalogExtractor();
+ }
+
+ return vnfCatalogExtractor;
+ }
}
public NotificationPublisher() {
Properties configProperties = new Properties();
try {
- configProperties.load(Files.newInputStream(Paths.get(ModelLoaderConfig.propertiesFile())));
+ configProperties.load(Files.newInputStream(ModelLoaderConfig.propertiesFile()));
} catch (IOException e) {
String errorMsg = "Failed to load configuration: " + e.getMessage();
logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, e, errorMsg);
-/**\r
- * ============LICENSE_START=======================================================\r
- * org.onap.aai\r
- * ================================================================================\r
- * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.\r
- * Copyright © 2017-2018 European Software Marketing Ltd.\r
- * ================================================================================\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- * ============LICENSE_END=========================================================\r
- */\r
-package org.onap.aai.modelloader.service;\r
-\r
-import com.att.eelf.i18n.EELFResourceManager;\r
-import org.onap.aai.cl.eelf.LogMessageEnum;\r
-\r
-public enum ModelLoaderMsgs implements LogMessageEnum {\r
-\r
- /**\r
- * Arguments: None.\r
- */\r
- LOADING_CONFIGURATION,\r
-\r
- /**\r
- * Arguments: None.\r
- */\r
- STOPPING_CLIENT,\r
-\r
- /**\r
- * Arguments: {0} = message.\r
- */\r
- INITIALIZING,\r
-\r
- /**\r
- * Arguments: {0} = reason.\r
- */\r
- ASDC_CONNECTION_ERROR,\r
-\r
- /**\r
- * Arguments: {0} = message.\r
- */\r
- DISTRIBUTION_EVENT,\r
-\r
- /**\r
- * Arguments: {0} = error message.\r
- */\r
- DISTRIBUTION_EVENT_ERROR,\r
-\r
- /**\r
- * Arguments: {0} = request type. {1} = endpoint. {2} = result code.\r
- */\r
- AAI_REST_REQUEST_SUCCESS,\r
-\r
- /**\r
- * Arguments: {0} = request type. {1} = endpoint. {2} = result code. {3} = result. message\r
- */\r
- AAI_REST_REQUEST_UNSUCCESSFUL,\r
-\r
- /**\r
- * Arguments: {0} = request type. {1} = endpoint. {2} = error message.\r
- */\r
- AAI_REST_REQUEST_ERROR,\r
-\r
- /**\r
- * Arguments: {0} = request type. {1} = endpoint. {2} = error message.\r
- */\r
- BABEL_REST_REQUEST_ERROR,\r
-\r
- /**\r
- * Arguments: {0} = info request payload.\r
- **/\r
- AAI_REST_REQUEST_PAYLOAD,\r
-\r
- /**\r
- * Arguments: {0} = artifact name\r
- */\r
- ARTIFACT_PARSE_ERROR,\r
-\r
- /**\r
- * Arguments: {0} = info request payload.\r
- **/\r
- BABEL_REST_REQUEST_PAYLOAD,\r
-\r
- /**\r
- * Arguments: {0} = info Babel response payload.\r
- **/\r
- BABEL_RESPONSE_PAYLOAD,\r
-\r
- /**\r
- * Arguments: {0} = artifact name. {1} = payload.\r
- */\r
- DOWNLOAD_COMPLETE,\r
-\r
- /**\r
- * Arguments: {0} = event. {1} = artifact name. {2} = result.\r
- */\r
- EVENT_PUBLISHED,\r
-\r
- /**\r
- * Arguments: {0} = artifact name. {1} = artifact type.\r
- */\r
- UNSUPPORTED_ARTIFACT_TYPE;\r
-\r
- /**\r
- * Load message bundle (ModelLoaderMsgs.properties file)\r
- */\r
- static {\r
- EELFResourceManager.loadMessageBundle("org/onap/aai/modelloader/service/ModelLoaderMsgs");\r
- }\r
-\r
-}\r
+/**
+ * ============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.modelloader.service;
+
+import com.att.eelf.i18n.EELFResourceManager;
+import org.onap.aai.cl.eelf.LogMessageEnum;
+
+public enum ModelLoaderMsgs implements LogMessageEnum {
+
+ /**
+ * Arguments: None.
+ */
+ LOADING_CONFIGURATION,
+
+ /**
+ * Arguments: None.
+ */
+ STOPPING_CLIENT,
+
+ /**
+ * Arguments: {0} = message.
+ */
+ INITIALIZING,
+
+ /**
+ * Arguments: {0} = reason.
+ */
+ ASDC_CONNECTION_ERROR,
+
+ /**
+ * Arguments: {0} = message.
+ */
+ DISTRIBUTION_EVENT,
+
+ /**
+ * Arguments: {0} = error message.
+ */
+ DISTRIBUTION_EVENT_ERROR,
+
+ /**
+ * Arguments: {0} = request type. {1} = endpoint. {2} = result code.
+ */
+ AAI_REST_REQUEST_SUCCESS,
+
+ /**
+ * Arguments: {0} = request type. {1} = endpoint. {2} = result code. {3} = result. message
+ */
+ AAI_REST_REQUEST_UNSUCCESSFUL,
+
+ /**
+ * Arguments: {0} = request type. {1} = endpoint. {2} = error message.
+ */
+ AAI_REST_REQUEST_ERROR,
+
+ /**
+ * Arguments: {0} = request type. {1} = endpoint. {2} = error message.
+ */
+ BABEL_REST_REQUEST_ERROR,
+
+ /**
+ * Arguments: {0} = info request payload.
+ **/
+ AAI_REST_REQUEST_PAYLOAD,
+
+ /**
+ * Arguments: {0} = artifact name
+ */
+ ARTIFACT_PARSE_ERROR,
+
+ /**
+ * Arguments: {0} = info request payload.
+ **/
+ BABEL_REST_REQUEST_PAYLOAD,
+
+ /**
+ * Arguments: {0} = info Babel response payload.
+ **/
+ BABEL_RESPONSE_PAYLOAD,
+
+ /**
+ * Arguments: {0} = artifact name. {1} = payload.
+ */
+ DOWNLOAD_COMPLETE,
+
+ /**
+ * Arguments: {0} = event. {1} = artifact name. {2} = result.
+ */
+ EVENT_PUBLISHED,
+
+ /**
+ * Arguments: {0} = artifact name. {1} = artifact type.
+ */
+ UNSUPPORTED_ARTIFACT_TYPE,
+
+ /**
+ * Arguments: {0} = artifact name.
+ */
+ DUPLICATE_VNFC_DATA_ERROR;
+
+ /**
+ * Load message bundle (ModelLoaderMsgs.properties file)
+ */
+ static {
+ EELFResourceManager.loadMessageBundle("org/onap/aai/modelloader/service/ModelLoaderMsgs");
+ }
+
+}
try {
new JSONObject(text);
isValid = true;
- } catch (JSONException ex) {
+ } catch (JSONException ex) { // NOSONAR
try {
new JSONArray(text);
isValid = true;
- } catch (JSONException ex1) {
+ } catch (JSONException ex1) { // NOSONAR
isValid = false;
}
}
Failed to send {0} request to {1}: {2}|\\r
Check configuration. Check network connection to Babel.|\\r
A failure occurred attempting to send a request to the Babel|\\r
+\r
+DUPLICATE_VNFC_DATA_ERROR=\\r
+ MDLSVC2006E|\\r
+ Artifact: {0} contains both TOSCA and XML VNF Catalog data.|\\r
+ Check artifact. |\\r
+ A failure occurred attempting to process VNF Catalog data in artifact from SDC|\\r
\r
# DEBUG Level Logs \r
INITIALIZING=\\r
--- /dev/null
+/**
+ * ============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.modelloader.csar.extractor;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.onap.aai.modelloader.entity.Artifact;
+import org.onap.aai.modelloader.entity.ArtifactType;
+import org.onap.aai.modelloader.extraction.InvalidArchiveException;
+import org.onap.aai.modelloader.extraction.VnfCatalogExtractor;
+import org.onap.aai.modelloader.util.ArtifactTestUtils;
+
+
+/**
+ * Tests {@link VnfCatalogExtractor}.
+ */
+public class VnfCatalogExtractorTest {
+
+ private static final String FOO = "foo";
+ private static final String SOME_BYTES = "just some bytes that will pass the firsts validation";
+ private static final String SUPPLY_AN_ARCHIVE = "An archive must be supplied for processing.";
+ private static final String SUPPLY_NAME = "The name of the archive must be supplied for processing.";
+
+ @Test
+ public void nullContentSupplied() {
+ invalidArgumentsTest(null, FOO, SUPPLY_AN_ARCHIVE);
+ }
+
+ @Test
+ public void emptyContentSupplied() {
+ invalidArgumentsTest(new byte[0], FOO, SUPPLY_AN_ARCHIVE);
+ }
+
+ @Test
+ public void nullNameSupplied() {
+ invalidArgumentsTest(SOME_BYTES.getBytes(), null, SUPPLY_NAME);
+ }
+
+ @Test
+ public void blankNameSupplied() {
+ invalidArgumentsTest("just some bytes that will pass the firsts validation".getBytes(), " \t ", SUPPLY_NAME);
+ }
+
+ @Test
+ public void emptyNameSupplied() {
+ invalidArgumentsTest("just some bytes that will pass the firsts validation".getBytes(), "", SUPPLY_NAME);
+ }
+
+ @Test
+ public void invalidContentSupplied() {
+ invalidArgumentsTest("This is a piece of nonsense and not a zip file".getBytes(), FOO,
+ "An error occurred trying to create a ZipFile. Is the content being converted really a csar file?");
+ }
+
+ @Test
+ public void archiveContainsNoVnfcFiles() throws InvalidArchiveException, IOException {
+ List<Artifact> vnfcArtifacts = new VnfCatalogExtractor().extract(
+ new ArtifactTestUtils().loadResource("compressedArtifacts/noVnfcFilesArchive.csar"),
+ "noVnfcFilesArchive.csar");
+ assertTrue("No VNFC files should have been extracted, but " + vnfcArtifacts.size() + " were found.",
+ vnfcArtifacts.isEmpty());
+ }
+
+ @Test
+ public void archiveContainsThreeRelevantVnfcFiles() throws IOException, InvalidArchiveException {
+ List<String> payloads = new ArrayList<>();
+ payloads.add("xmlFiles/vnfcatalog-1.xml");
+ payloads.add("xmlFiles/vnfcatalog-2.xml");
+ payloads.add("xmlFiles/vnfcatalog-3.xml");
+ String csarArchiveFile = "threeVnfcFilesArchive.csar";
+ performVnfcAsserts(new VnfCatalogExtractor().extract(
+ new ArtifactTestUtils().loadResource("compressedArtifacts/" + csarArchiveFile), csarArchiveFile),
+ payloads);
+ }
+
+ public void performVnfcAsserts(List<Artifact> actualVnfcArtifacts, List<String> expectedVnfcPayloadsToLoad) {
+ assertThat("An unexpected number of VNFC files have been extracted", actualVnfcArtifacts.size(),
+ is(expectedVnfcPayloadsToLoad.size()));
+
+ for (Artifact artifact : actualVnfcArtifacts) {
+ assertThat("Unexpected artifact type found.", artifact.getType(), is(ArtifactType.VNF_CATALOG_XML));
+ }
+
+ Set<String> expectedVnfcPayloads = expectedVnfcPayloadsToLoad.stream().map(s -> {
+ try {
+ return ArtifactTestUtils.loadResourceAsString(s);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }).collect(Collectors.toSet());
+
+ Set<String> actualVnfcPayloads =
+ actualVnfcArtifacts.stream().map(s -> s.getPayload()).collect(Collectors.toSet());
+
+ assertThat("Unexpected VNF Catalog file(s) found.", expectedVnfcPayloads.containsAll(actualVnfcPayloads),
+ is(true));
+ }
+
+ private void invalidArgumentsTest(byte[] archive, String name, String expectedErrorMessage) {
+ try {
+ new VnfCatalogExtractor().extract(archive, name);
+ fail("An instance of InvalidArchiveException should have been thrown");
+ } catch (Exception ex) {
+ assertTrue(ex instanceof InvalidArchiveException);
+ assertEquals(expectedErrorMessage, ex.getLocalizedMessage());
+ }
+ }
+
+}
+
import org.mockito.Mockito;
import org.onap.aai.modelloader.config.ModelLoaderConfig;
import org.onap.aai.modelloader.entity.Artifact;
+import org.onap.aai.modelloader.entity.ArtifactType;
import org.onap.aai.modelloader.restclient.AaiRestClient;
import org.onap.aai.restclient.client.OperationResult;
assertPutOperationsSucceeded();
}
+ @Test
+ public void testUpdateVnfImagesFromXml() throws Exception {
+ // GET operation
+ OperationResult mockGetResp = mock(OperationResult.class);
+
+ // @formatter:off
+ when(mockGetResp.getResultCode())
+ .thenReturn(Response.Status.OK.getStatusCode())
+ .thenReturn(Response.Status.NOT_FOUND.getStatusCode())
+ .thenReturn(Response.Status.NOT_FOUND.getStatusCode())
+ .thenReturn(Response.Status.OK.getStatusCode());
+ // @formatter:on
+
+ when(mockRestClient.getResource(Mockito.anyString(), Mockito.anyString(), Mockito.any(MediaType.class)))
+ .thenReturn(mockGetResp);
+ mockPutOperations();
+
+ // Example VNF Catalog XML
+ VnfCatalogArtifactHandler handler = new VnfCatalogArtifactHandler(createConfig());
+ assertThat(
+ handler.pushArtifacts(createVnfCatalogXmlArtifact(), "test", new ArrayList<Artifact>(), mockRestClient),
+ is(true));
+
+ // Only two of the VNF images should be pushed
+ ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
+ AaiRestClient client = Mockito.verify(mockRestClient, Mockito.times(2));
+ client.putResource(Mockito.anyString(), argument.capture(), Mockito.anyString(), Mockito.any(MediaType.class));
+ assertThat(argument.getAllValues().size(), is(2));
+ assertThat(argument.getAllValues().get(0), containsString("5.2.5"));
+ assertThat(argument.getAllValues().get(0), containsString("VM00"));
+ assertThat(argument.getAllValues().get(1), containsString("5.2.4"));
+ assertThat(argument.getAllValues().get(1), containsString("VM00"));
+ }
+
private ModelLoaderConfig createConfig() {
Properties configProperties = new Properties();
try {
return artifacts;
}
+ /**
+ * Example VNF Catalog based on VNF_CATALOG XML
+ *
+ * @return test Artifacts
+ * @throws IOException
+ * @throws UnsupportedEncodingException
+ */
+ private List<Artifact> createVnfCatalogXmlArtifact() throws IOException, UnsupportedEncodingException {
+ byte[] encoded = Files.readAllBytes(Paths.get("src/test/resources/xmlFiles/fortigate.xml"));
+ List<Artifact> artifacts = new ArrayList<Artifact>();
+ artifacts.add(new VnfCatalogArtifact(ArtifactType.VNF_CATALOG_XML, new String(encoded, "utf-8")));
+ return artifacts;
+ }
+
/**
* Always return CREATED (success) for a PUT operation.
*/
--- /dev/null
+/**
+ * ============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.modelloader.notification;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.onap.aai.modelloader.fixture.NotificationDataFixtureBuilder.getNotificationDataWithToscaCsarFile;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.Whitebox;
+import org.onap.aai.babel.service.data.BabelArtifact;
+import org.onap.aai.modelloader.config.ModelLoaderConfig;
+import org.onap.aai.modelloader.entity.Artifact;
+import org.onap.aai.modelloader.entity.ArtifactType;
+import org.onap.aai.modelloader.entity.catalog.VnfCatalogArtifact;
+import org.onap.aai.modelloader.entity.model.BabelArtifactParsingException;
+import org.onap.aai.modelloader.entity.model.ModelArtifact;
+import org.onap.aai.modelloader.extraction.InvalidArchiveException;
+import org.onap.aai.modelloader.extraction.VnfCatalogExtractor;
+import org.onap.aai.modelloader.restclient.BabelServiceClient;
+import org.onap.aai.modelloader.restclient.BabelServiceClientException;
+import org.onap.aai.modelloader.service.BabelServiceClientFactory;
+import org.onap.aai.modelloader.util.ArtifactTestUtils;
+import org.onap.sdc.api.IDistributionClient;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.notification.INotificationData;
+import org.onap.sdc.api.results.IDistributionClientDownloadResult;
+import org.onap.sdc.impl.DistributionClientDownloadResultImpl;
+import org.onap.sdc.utils.DistributionActionResultEnum;
+
+/**
+ * Tests {@link ArtifactDownloadManager} with VNF Catalog Artifacts.
+ */
+public class ArtifactDownloadManagerVnfcTest {
+
+ private ArtifactDownloadManager downloadManager;
+ private BabelServiceClient mockBabelClient;
+ private IDistributionClient mockDistributionClient;
+ private NotificationPublisher mockNotificationPublisher;
+ private BabelArtifactConverter mockBabelArtifactConverter;
+ private BabelServiceClientFactory mockClientFactory;
+ private VnfCatalogExtractor mockVnfCatalogExtractor;
+
+ @Before
+ public void setup() throws Exception {
+ mockBabelClient = mock(BabelServiceClient.class);
+ mockDistributionClient = mock(IDistributionClient.class);
+ mockNotificationPublisher = mock(NotificationPublisher.class);
+ mockBabelArtifactConverter = mock(BabelArtifactConverter.class);
+ mockClientFactory = mock(BabelServiceClientFactory.class);
+ when(mockClientFactory.create(Mockito.any())).thenReturn(mockBabelClient);
+ mockVnfCatalogExtractor = mock(VnfCatalogExtractor.class);
+
+ Properties configProperties = new Properties();
+ configProperties.load(this.getClass().getClassLoader().getResourceAsStream("model-loader.properties"));
+ downloadManager = new ArtifactDownloadManager(mockDistributionClient,
+ new ModelLoaderConfig(configProperties, "."), mockClientFactory);
+
+
+ Whitebox.setInternalState(downloadManager, "notificationPublisher", mockNotificationPublisher);
+ Whitebox.setInternalState(downloadManager, "babelArtifactConverter", mockBabelArtifactConverter);
+ Whitebox.setInternalState(downloadManager, "vnfCatalogExtractor", mockVnfCatalogExtractor);
+ }
+
+ @Test
+ public void downloadArtifacts_validToscaVnfcCsarFile()
+ throws IOException, BabelServiceClientException, BabelArtifactParsingException, InvalidArchiveException {
+ INotificationData data = getNotificationDataWithToscaCsarFile();
+ IArtifactInfo artifactInfo = data.getServiceArtifacts().get(0);
+
+ setupValidDownloadCsarMocks(data, artifactInfo);
+ when(mockBabelClient.postArtifact(Matchers.any(), Matchers.anyString(), Matchers.anyString(),
+ Matchers.anyString())).thenReturn(createBabelArtifacts());
+ when(mockVnfCatalogExtractor.extract(Matchers.any(), Matchers.anyString())).thenReturn(new ArrayList<>());
+
+ List<Artifact> modelArtifacts = new ArrayList<>();
+ List<Artifact> catalogFiles = new ArrayList<>();
+ assertThat(downloadManager.downloadArtifacts(data, data.getServiceArtifacts(), modelArtifacts, catalogFiles),
+ is(true));
+
+ assertEquals("There should have been some catalog files", 2, catalogFiles.size());
+ }
+
+ @Test
+ public void downloadArtifacts_validXmlVnfcCsarFile()
+ throws IOException, BabelServiceClientException, BabelArtifactParsingException, InvalidArchiveException {
+ INotificationData data = getNotificationDataWithToscaCsarFile();
+ IArtifactInfo artifactInfo = data.getServiceArtifacts().get(0);
+
+ setupValidDownloadCsarMocks(data, artifactInfo);
+ when(mockBabelClient.postArtifact(Matchers.any(), Matchers.anyString(), Matchers.anyString(),
+ Matchers.anyString())).thenReturn(createBabelArtifactsNoVnfc());
+ when(mockVnfCatalogExtractor.extract(Matchers.any(), Matchers.anyString()))
+ .thenReturn(createXmlVnfcArtifacts());
+
+ List<Artifact> modelArtifacts = new ArrayList<>();
+ List<Artifact> catalogFiles = new ArrayList<>();
+ assertThat(downloadManager.downloadArtifacts(data, data.getServiceArtifacts(), modelArtifacts, catalogFiles),
+ is(true));
+
+ assertEquals("There should have been some catalog files", 3, catalogFiles.size());
+ }
+
+ @Test
+ public void downloadArtifacts_validNoVnfcCsarFile()
+ throws IOException, BabelServiceClientException, BabelArtifactParsingException, InvalidArchiveException {
+ INotificationData data = getNotificationDataWithToscaCsarFile();
+ IArtifactInfo artifactInfo = data.getServiceArtifacts().get(0);
+
+ setupValidDownloadCsarMocks(data, artifactInfo);
+ when(mockBabelClient.postArtifact(Matchers.any(), Matchers.anyString(), Matchers.anyString(),
+ Matchers.anyString())).thenReturn(createBabelArtifactsNoVnfc());
+ when(mockVnfCatalogExtractor.extract(Matchers.any(), Matchers.anyString())).thenReturn(new ArrayList<>());
+
+ List<Artifact> modelArtifacts = new ArrayList<>();
+ List<Artifact> catalogFiles = new ArrayList<>();
+ assertThat(downloadManager.downloadArtifacts(data, data.getServiceArtifacts(), modelArtifacts, catalogFiles),
+ is(true));
+
+ assertEquals("There should not have been any catalog files", 0, catalogFiles.size());
+ }
+
+ @Test
+ public void downloadArtifacts_invalidXmlAndToscaVnfcCsarFile()
+ throws IOException, BabelServiceClientException, BabelArtifactParsingException, InvalidArchiveException {
+ INotificationData data = getNotificationDataWithToscaCsarFile();
+ IArtifactInfo artifactInfo = data.getServiceArtifacts().get(0);
+
+ setupValidDownloadCsarMocks(data, artifactInfo);
+ when(mockBabelClient.postArtifact(Matchers.any(), Matchers.anyString(), Matchers.anyString(),
+ Matchers.anyString())).thenReturn(createBabelArtifacts());
+ when(mockVnfCatalogExtractor.extract(Matchers.any(), Matchers.anyString()))
+ .thenReturn(createXmlVnfcArtifacts());
+ doNothing().when(mockNotificationPublisher).publishDeployFailure(mockDistributionClient, data, artifactInfo);
+
+ List<Artifact> modelArtifacts = new ArrayList<>();
+ List<Artifact> catalogFiles = new ArrayList<>();
+ assertThat(downloadManager.downloadArtifacts(data, data.getServiceArtifacts(), modelArtifacts, catalogFiles),
+ is(false));
+
+ Mockito.verify(mockNotificationPublisher).publishDeployFailure(mockDistributionClient, data, artifactInfo);
+ }
+
+ private IDistributionClientDownloadResult createDistributionClientDownloadResult(
+ DistributionActionResultEnum status, String message, byte[] payload) {
+ IDistributionClientDownloadResult downloadResult = new DistributionClientDownloadResultImpl(status, message);
+
+ ((DistributionClientDownloadResultImpl) downloadResult).setArtifactPayload(payload);
+
+ return downloadResult;
+ }
+
+ private void setupValidDownloadCsarMocks(INotificationData data, IArtifactInfo artifactInfo)
+ throws IOException, BabelServiceClientException, BabelArtifactParsingException {
+ when(mockDistributionClient.download(artifactInfo))
+ .thenReturn(createDistributionClientDownloadResult(DistributionActionResultEnum.SUCCESS, null,
+ new ArtifactTestUtils().loadResource("compressedArtifacts/service-VscpaasTest-csar.csar")));
+ when(mockBabelArtifactConverter.convertToModel(Mockito.anyListOf(BabelArtifact.class)))
+ .thenReturn(createModelArtifacts());
+ when(mockBabelArtifactConverter.convertToCatalog(Mockito.anyListOf(BabelArtifact.class)))
+ .thenReturn(createToscaVnfcArtifacts());
+ }
+
+ private List<BabelArtifact> createBabelArtifacts() {
+ List<BabelArtifact> artifactList = new ArrayList<>();
+ artifactList.add(new BabelArtifact("ModelArtifact", BabelArtifact.ArtifactType.MODEL, "Some model payload"));
+ artifactList.add(new BabelArtifact("VNFCArtifact", BabelArtifact.ArtifactType.VNFCATALOG, "Some VNFC payload"));
+ return artifactList;
+ }
+
+ private List<BabelArtifact> createBabelArtifactsNoVnfc() {
+ List<BabelArtifact> artifactList = new ArrayList<>();
+ artifactList.add(new BabelArtifact("ModelArtifact", BabelArtifact.ArtifactType.MODEL, "Some model payload"));
+ return artifactList;
+ }
+
+ private List<Artifact> createModelArtifacts() {
+ List<Artifact> modelArtifacts = new ArrayList<>();
+ modelArtifacts.add(new ModelArtifact());
+ modelArtifacts.add(new ModelArtifact());
+ return modelArtifacts;
+ }
+
+ private List<Artifact> createToscaVnfcArtifacts() {
+ List<Artifact> vnfcArtifacts = new ArrayList<>();
+ vnfcArtifacts.add(new VnfCatalogArtifact("Some VNFC payload"));
+ vnfcArtifacts.add(new VnfCatalogArtifact("Some VNFC payload"));
+ return vnfcArtifacts;
+ }
+
+ private List<Artifact> createXmlVnfcArtifacts() {
+ List<Artifact> vnfcArtifacts = new ArrayList<>();
+ vnfcArtifacts.add(new VnfCatalogArtifact(ArtifactType.VNF_CATALOG_XML, "Some VNFC payload"));
+ vnfcArtifacts.add(new VnfCatalogArtifact(ArtifactType.VNF_CATALOG_XML, "Some VNFC payload"));
+ vnfcArtifacts.add(new VnfCatalogArtifact(ArtifactType.VNF_CATALOG_XML, "Some VNFC payload"));
+ return vnfcArtifacts;
+ }
+}
public void testMissingConfig() {
new ModelLoaderService().start();
}
-
+
@Test
public void testLoadModel() {
Response response = service.loadModel("");
public void testIngestModelMissingName() throws IOException {
byte[] csarPayload = new ArtifactTestUtils().loadResource("compressedArtifacts/service-VscpaasTest-csar.csar");
Response response = service.ingestModel("", "", Base64.getEncoder().encodeToString(csarPayload));
- assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+ assertThat(response.getStatus(), is(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()));
}
}
@Test
public void testConversion() throws Exception {
- byte[] encoded = Files.readAllBytes(Paths.get(XML_MODEL_FILE));
- assertThat(JsonXmlConverter.isValidJson(new String(encoded)), is(false));
- encoded = Files.readAllBytes(Paths.get(JSON_MODEL_FILE));
+ byte[] encoded = Files.readAllBytes(Paths.get(XML_MODEL_FILE));
+ assertThat(JsonXmlConverter.isValidJson(new String(encoded)), is(false));
+ encoded = Files.readAllBytes(Paths.get(JSON_MODEL_FILE));
String originalJson = new String(encoded);
assertThat(JsonXmlConverter.isValidJson(originalJson), is(true));
String xmlFromJson = JsonXmlConverter.convertJsonToXml(originalJson);
- // Spot check one of the attributes
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- DocumentBuilder builder = factory.newDocumentBuilder();
- Document doc = builder.parse(new ByteArrayInputStream(xmlFromJson.getBytes()));
- NodeList nodeList = doc.getDocumentElement().getChildNodes();
+ // Spot check one of the attributes
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(new ByteArrayInputStream(xmlFromJson.getBytes()));
+ NodeList nodeList = doc.getDocumentElement().getChildNodes();
- String modelVid = "notFound";
- for (int i = 0; i < nodeList.getLength(); i++) {
- Node currentNode = nodeList.item(i);
- if (currentNode.getNodeName().equals("model-invariant-id")) {
- modelVid = currentNode.getTextContent();
- break;
- }
+ String modelVid = "notFound";
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node currentNode = nodeList.item(i);
+ if (currentNode.getNodeName().equals("model-invariant-id")) {
+ modelVid = currentNode.getTextContent();
+ break;
}
+ }
assertThat(modelVid.equals("3d560d81-57d0-438b-a2a1-5334dba0651a"), is(true));
- // Convert the XML form back into JSON
- JsonXmlConverter.convertXmlToJson(xmlFromJson);
+ // Convert the XML form back into JSON
+ JsonXmlConverter.convertXmlToJson(xmlFromJson);
}
}
--- /dev/null
+<vnf-catalog>
+ <part-number-list>
+ <part-number>FortiGate-VM00</part-number>
+ <vnf-type>FW</vnf-type>
+ <vendor-info>
+ <vendor-name>FORTINET</vendor-name>
+ <vendor-part-number>FortiGate-VM00</vendor-part-number>
+ <vendor-model>VM00</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>1</vcpu-default>
+ <vcpu-min>1</vcpu-min>
+ <vcpu-max>1</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>1</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>1</vmemory-min>
+ <vmemory-max>1</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>2</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>2</vdisk-min>
+ <vdisk-max>32</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>5.2.7</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>1c59a521885c465004456f74d003726c</file-md5-value>
+ <software-filename>test3</software-filename>
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.5</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>C4D2CBE51669796E48623E006782F7DC</file-md5-value>
+ <software-filename>test2</software-filename>
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>4987E1E743FD641C879E1D3C5D50BCE0</file-md5-value>
+ <software-filename>test1</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>FortiGate-VM00</license-assignment-group>
+ <license-required>FALSE</license-required>
+ </license-list>
+ </part-number-list>
+ <part-number-list>
+ <part-number>FortiGate-VM01</part-number>
+ <vnf-type>FW</vnf-type>
+ <vendor-info>
+ <vendor-name>FORTINET</vendor-name>
+ <vendor-part-number>FortiGate-VM01</vendor-part-number>
+ <vendor-model>VM01</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>1</vcpu-default>
+ <vcpu-min>1</vcpu-min>
+ <vcpu-max>1</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>2</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>1</vmemory-min>
+ <vmemory-max>2</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>2</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>2</vdisk-min>
+ <vdisk-max>32</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>5.2.7</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>1c59a521885c465004456f74d003726c</file-md5-value>
+ <software-filename>software file name 3</software-filename>
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.5</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>C4D2CBE51669796E48623E006782F7DC</file-md5-value>
+ <software-filename>software file name 2</software-filename>
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>4987E1E743FD641C879E1D3C5D50BCE0</file-md5-value>
+ <software-filename>software file name</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license group</license-assignment-group>
+ <license-required>FALSE</license-required>
+ </license-list>
+ </part-number-list>
+
+</vnf-catalog>
\ No newline at end of file
--- /dev/null
+<vnf-catalog>
+ <part-number-list>
+ <att-part-number>att-part-number1</att-part-number>
+ <vnf-type>vnf-type1</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name1</vendor-name>
+ <vendor-part-number>vendor-part-number1</vendor-part-number>
+ <vendor-model>vendor-model1</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>2</vcpu-default>
+ <vcpu-min>2</vcpu-min>
+ <vcpu-max>2</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>2</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>2</vmemory-min>
+ <vmemory-max>2</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version1</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename1</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group1</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+ <part-number-list>
+ <att-part-number>att-part-number2</att-part-number>
+ <vnf-type>vnf-type2</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name2</vendor-name>
+ <vendor-part-number>vendor-part-number2</vendor-part-number>
+ <vendor-model>vendor-model2</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>2</vcpu-default>
+ <vcpu-min>2</vcpu-min>
+ <vcpu-max>2</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>2</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>2</vmemory-min>
+ <vmemory-max>2</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version2</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename2</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group2</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+</vnf-catalog>
\ No newline at end of file
--- /dev/null
+<vnf-catalog>
+ <part-number-list>
+ <att-part-number>att-part-number3</att-part-number>
+ <vnf-type>vnf-type3</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name3</vendor-name>
+ <vendor-part-number>vendor-part-number3</vendor-part-number>
+ <vendor-model>vendor-model3</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>2</vcpu-default>
+ <vcpu-min>2</vcpu-min>
+ <vcpu-max>2</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>2</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>2</vmemory-min>
+ <vmemory-max>2</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version3</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename3</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group3</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+ <part-number-list>
+ <att-part-number>att-part-number4</att-part-number>
+ <vnf-type>vnf-type4</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name4</vendor-name>
+ <vendor-part-number>vendor-part-number4</vendor-part-number>
+ <vendor-model>vendor-model4</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>4</vcpu-default>
+ <vcpu-min>4</vcpu-min>
+ <vcpu-max>4</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>4</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>4</vmemory-min>
+ <vmemory-max>4</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename4</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group4</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+</vnf-catalog>
\ No newline at end of file
--- /dev/null
+<vnf-catalog>
+ <part-number-list>
+ <att-part-number>att-part-number5</att-part-number>
+ <vnf-type>vnf-type5</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name5</vendor-name>
+ <vendor-part-number>vendor-part-number5</vendor-part-number>
+ <vendor-model>vendor-model5</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>2</vcpu-default>
+ <vcpu-min>2</vcpu-min>
+ <vcpu-max>2</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>2</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>2</vmemory-min>
+ <vmemory-max>2</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version5</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename5</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group5</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+ <part-number-list>
+ <att-part-number>att-part-number6</att-part-number>
+ <vnf-type>vnf-type6</vnf-type>
+ <vendor-info>
+ <vendor-name>vendor-name6</vendor-name>
+ <vendor-part-number>vendor-part-number6</vendor-part-number>
+ <vendor-model>vendor-model6</vendor-model>
+ </vendor-info>
+ <vcpu>
+ <vcpu-default>6</vcpu-default>
+ <vcpu-min>6</vcpu-min>
+ <vcpu-max>6</vcpu-max>
+ </vcpu>
+ <vmemory>
+ <vmemory-default>6</vmemory-default>
+ <vmemory-units>GB</vmemory-units>
+ <vmemory-min>6</vmemory-min>
+ <vmemory-max>6</vmemory-max>
+ </vmemory>
+ <vdisk>
+ <vdisk-default>50</vdisk-default>
+ <vdisk-units>GB</vdisk-units>
+ <vdisk-min>50</vdisk-min>
+ <vdisk-max>50</vdisk-max>
+ </vdisk>
+ <software-version-list>
+ <software-version>software-version6</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filetype>IMAGE</software-filetype>
+ <file-md5-value>BE2B249315B4410896099CFD1AE1948C</file-md5-value>
+ <software-filename>software-filename6</software-filename>
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>APPID</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <license-list>
+ <license-assignment-group>license-assignment-group6</license-assignment-group>
+ <license-required>TRUE</license-required>
+ </license-list>
+ </part-number-list>
+</vnf-catalog>
\ No newline at end of file