/**\r
- * ============LICENSE_START==========================================\r
+ * ============LICENSE_START=======================================================\r
* org.onap.aai\r
- * ===================================================================\r
+ * ================================================================================\r
* Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.\r
- * Copyright © 2017-2018 Amdocs\r
- * ===================================================================\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
+ * 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
+ * ============LICENSE_END=========================================================\r
*/\r
package org.onap.aai.modelloader.entity.catalog;\r
\r
-import com.sun.jersey.api.client.ClientResponse;\r
-\r
-import generated.VnfCatalog;\r
-import generated.VnfCatalog.PartNumberList;\r
-\r
-import inventory.aai.openecomp.org.v8.VnfImage;\r
-\r
-import org.eclipse.persistence.jaxb.MarshallerProperties;\r
+import com.google.gson.Gson;\r
+import com.google.gson.reflect.TypeToken;\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.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.entity.Artifact;\r
import org.onap.aai.modelloader.entity.ArtifactHandler;\r
+import org.onap.aai.modelloader.entity.vnf.VnfImages;\r
import org.onap.aai.modelloader.restclient.AaiRestClient;\r
-import org.onap.aai.modelloader.restclient.AaiRestClient.MimeType;\r
import org.onap.aai.modelloader.service.ModelLoaderMsgs;\r
-import org.onap.aai.cl.api.Logger;\r
-import org.onap.aai.cl.eelf.LoggerFactory;\r
-import org.springframework.web.util.UriUtils;\r
-\r
-import java.io.StringReader;\r
-import java.io.StringWriter;\r
-import java.io.UnsupportedEncodingException;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-import java.util.UUID;\r
+import org.springframework.http.HttpStatus;\r
+import org.springframework.http.MediaType;\r
+import org.springframework.http.ResponseEntity;\r
+import org.springframework.stereotype.Component;\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
-import javax.ws.rs.core.Response;\r
-import javax.xml.bind.JAXBContext;\r
-import javax.xml.bind.JAXBException;\r
-import javax.xml.bind.Marshaller;\r
-import javax.xml.bind.Unmarshaller;\r
+/**\r
+ * VNF Catalog specific handling\r
+ */\r
+@Component\r
+public class VnfCatalogArtifactHandler extends ArtifactHandler {\r
\r
+ private static Logger logger = LoggerFactory.getInstance().getLogger(VnfCatalogArtifactHandler.class.getName());\r
\r
-public class VnfCatalogArtifactHandler extends ArtifactHandler {\r
+ public static final String ATTR_UUID = "uuid";\r
\r
- private static Logger logger = LoggerFactory.getInstance()\r
- .getLogger(VnfCatalogArtifactHandler.class.getName());\r
+ public VnfCatalogArtifactHandler(ModelLoaderConfig config) {\r
+ super(config);\r
+ }\r
\r
- public VnfCatalogArtifactHandler(ModelLoaderConfig config) {\r
- super(config);\r
- }\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see org.openecomp.modelloader.entity.ArtifactHandler#pushArtifacts(java.util.List, java.lang.String)\r
+ */\r
+ @Override\r
+ public boolean pushArtifacts(List<Artifact> artifacts, String distributionId, List<Artifact> completedArtifacts,\r
+ AaiRestClient aaiClient) {\r
+ for (Artifact artifact : artifacts) {\r
+ try {\r
+ distributeVnfcData(aaiClient, distributionId, artifact, completedArtifacts);\r
+ } catch (VnfImageException e) {\r
+ if (e.getResultCode().isPresent()) {\r
+ logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
+ "Ingestion failed on vnf-image " + e.getImageId() + " with status "\r
+ + e.getResultCode().orElse(0) + ". Rolling back distribution.");\r
+ } else {\r
+ logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
+ "Ingestion failed on " + e.getImageId() + ". Rolling back distribution.");\r
+ }\r
+ return false;\r
+ }\r
+ }\r
\r
- @Override\r
- public boolean pushArtifacts(List<Artifact> artifacts, String distributionId) {\r
- for (Artifact art : artifacts) {\r
- VnfCatalogArtifact vnfCatalog = (VnfCatalogArtifact) art;\r
- String artifactPayload = vnfCatalog.getPayload();\r
+ return true;\r
+ }\r
\r
- AaiRestClient restClient = new AaiRestClient(this.config);\r
- List<VnfImage> putImages = new ArrayList<VnfImage>();\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
- try {\r
- JAXBContext inputContext = JAXBContext.newInstance(VnfCatalog.class);\r
- Unmarshaller unmarshaller = inputContext.createUnmarshaller();\r
- StringReader reader = new StringReader(artifactPayload);\r
- VnfCatalog cat = (VnfCatalog) unmarshaller.unmarshal(reader);\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
- int numParts = cat.getPartNumberList().size();\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
+ logger.warn(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Empty image data supplied, skipping ingestion.");\r
+ continue;\r
+ }\r
\r
- for (int i = 0; i < numParts; i++) {\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
- PartNumberList pnl = cat.getPartNumberList().get(i);\r
+ if (resultCode == HttpStatus.NOT_FOUND.value()) {\r
+ // This vnf-image is missing, so add it\r
+ boolean success = putVnfImage(restClient, dataItem, distributionId);\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
+ } else if (resultCode == HttpStatus.OK.value()) {\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, resultCode);\r
+ }\r
+ }\r
+ }\r
\r
- String application = pnl.getVendorInfo().getVendorModel();\r
- String applicationVendor = pnl.getVendorInfo().getVendorName();\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
+ ResponseEntity<VnfImages> tryGet =\r
+ restClient.getResource(b.build().toString(), distributionId, MediaType.APPLICATION_JSON, VnfImages.class);\r
+ if (tryGet == null) {\r
+ throw new VnfImageException(imageId);\r
+ }\r
+ return tryGet.getStatusCodeValue();\r
+ } catch (URISyntaxException ex) {\r
+ throw new VnfImageException(ex);\r
+ }\r
+ }\r
\r
- int numVersions = pnl.getSoftwareVersionList().size();\r
+ private boolean putVnfImage(AaiRestClient restClient, Map<String, String> dataItem, String distributionId) {\r
+ // Generate a new UUID for the image data item\r
+ String uuid = UUID.randomUUID().toString();\r
+ dataItem.put(ATTR_UUID, uuid);\r
\r
- for (int j = 0; j < numVersions; j++) {\r
- String applicationVersion = pnl.getSoftwareVersionList().get(j).getSoftwareVersion();\r
+ // TODO: Get rid of the dataItem map and replace it with the VnfImage object\r
+ String payload = new Gson().toJson(dataItem);\r
+ String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + uuid;\r
+ ResponseEntity<String> putResp =\r
+ restClient.putResource(putUrl, payload, distributionId, MediaType.APPLICATION_JSON, String.class);\r
+ return putResp != null && putResp.getStatusCode() == HttpStatus.CREATED;\r
+ }\r
\r
- String imageId = "vnf image " + applicationVendor + " " + application + " "\r
- + applicationVersion;\r
+ private List<Map<String, String>> unmarshallVnfcData(Artifact vnfcArtifact) {\r
+ // Unmarshall Babel JSON payload into a List of Maps of JSON attribute name/values.\r
+ return new Gson().fromJson(StringEscapeUtils.unescapeJson(vnfcArtifact.getPayload()),\r
+ new TypeToken<List<Map<String, String>>>() {}.getType());\r
+ }\r
\r
- String queryURI = "application-vendor=" + applicationVendor + "&application=" + application + "&application-version=" + applicationVersion;\r
- \r
- String getUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "?" + UriUtils.encodePath(queryURI, "UTF-8");\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
+ 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
- ClientResponse tryGet = restClient.getResource(getUrl, distributionId, MimeType.JSON);\r
- if (tryGet == null) {\r
- logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
- "Ingestion failed on " + imageId + ". Rolling back distribution.");\r
- failureCleanup(putImages, restClient, distributionId);\r
- return false;\r
- }\r
- if (tryGet.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {\r
- // this vnf-image not already in the db, need to add\r
- // only do this on 404 bc other error responses could mean there\r
- // are problems that\r
- // you might not want to try to PUT against\r
-\r
- VnfImage image = new VnfImage();\r
- image.setApplication(application);\r
- image.setApplicationVendor(applicationVendor);\r
- image.setApplicationVersion(applicationVersion);\r
- String uuid = UUID.randomUUID().toString();\r
- image.setUuid(uuid); // need to create uuid\r
-\r
- System.setProperty("javax.xml.bind.context.factory",\r
- "org.eclipse.persistence.jaxb.JAXBContextFactory");\r
- JAXBContext jaxbContext = JAXBContext.newInstance(VnfImage.class);\r
- Marshaller marshaller = jaxbContext.createMarshaller();\r
- marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");\r
- marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);\r
- marshaller.setProperty(MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);\r
- marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);\r
- StringWriter writer = new StringWriter();\r
- marshaller.marshal(image, writer);\r
- String payload = writer.toString();\r
-\r
- String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/"\r
- + uuid;\r
-\r
- ClientResponse putResp = restClient.putResource(putUrl, payload, distributionId,\r
- MimeType.JSON);\r
- if (putResp == null\r
- || putResp.getStatus() != Response.Status.CREATED.getStatusCode()) {\r
- logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
- "Ingestion failed on vnf-image " + imageId + ". Rolling back distribution.");\r
- failureCleanup(putImages, restClient, distributionId);\r
- return false;\r
- }\r
- putImages.add(image);\r
- logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested.");\r
- } else if (tryGet.getStatus() == Response.Status.OK.getStatusCode()) {\r
- logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT,\r
- imageId + " already exists. Skipping ingestion.");\r
- } else {\r
- // if other than 404 or 200, something went wrong\r
- logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
- "Ingestion failed on vnf-image " + imageId + " with status " + tryGet.getStatus()\r
- + ". Rolling back distribution.");\r
- failureCleanup(putImages, restClient, distributionId);\r
- return false;\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
- }\r
+ } catch (Exception ex) {\r
+ throw new VnfImageException(ex);\r
}\r
+ return vnfcData;\r
+ }\r
\r
- } catch (JAXBException e) {\r
- logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
- "Ingestion failed. " + e.getMessage() + ". Rolling back distribution.");\r
- failureCleanup(putImages, restClient, distributionId);\r
- return false;\r
- } catch (UnsupportedEncodingException e) {\r
- logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Ingestion failed. " + e.getMessage() + ". Rolling back distribution.");\r
- failureCleanup(putImages, restClient, distributionId);\r
- return false;\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
- return true;\r
- }\r
-\r
- /*\r
- * if something fails in the middle of ingesting the catalog we want to\r
- * rollback any changes to the db\r
- */\r
- private void failureCleanup(List<VnfImage> putImages, AaiRestClient restClient, String transId) {\r
- for (VnfImage image : putImages) {\r
- String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/"\r
- + image.getUuid();\r
- restClient.getAndDeleteResource(url, transId); // try to delete the image,\r
- // if something goes wrong\r
- // we can't really do\r
- // anything here\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
}\r