Add support for loading VNF Catalog XML files
[aai/model-loader.git] / src / main / java / org / onap / aai / modelloader / entity / catalog / VnfCatalogArtifactHandler.java
index 3480c68..c54d7b2 100644 (file)
@@ -1,5 +1,5 @@
 /**\r
- * ============LICENSE_START=======================================================\r
+ * ============LICENSE_START=======================================================\r
  * org.onap.aai\r
  * ================================================================================\r
  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.\r
@@ -22,14 +22,20 @@ package org.onap.aai.modelloader.entity.catalog;
 \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
@@ -38,7 +44,11 @@ import org.onap.aai.modelloader.entity.ArtifactHandler;
 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
@@ -80,11 +90,47 @@ public class VnfCatalogArtifactHandler extends ArtifactHandler {
         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
@@ -92,49 +138,47 @@ public class VnfCatalogArtifactHandler extends ArtifactHandler {
                 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
@@ -155,19 +199,79 @@ public class VnfCatalogArtifactHandler extends ArtifactHandler {
                 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