X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Forg%2Fonap%2Faai%2Fmodelloader%2Fentity%2Fcatalog%2FVnfCatalogArtifactHandler.java;h=c54d7b2df5f7b079624679e3b0fd3123f0a689e9;hb=7e6fe8c29c5a5cfa5caf6ab47b30280e1fc20432;hp=9ef3f6f39b0b21ee900b082b99d2c0e5a0c1986f;hpb=059f42e1a2289ecb2cb5835b8b14a0d098c9e1d3;p=aai%2Fmodel-loader.git diff --git a/src/main/java/org/onap/aai/modelloader/entity/catalog/VnfCatalogArtifactHandler.java b/src/main/java/org/onap/aai/modelloader/entity/catalog/VnfCatalogArtifactHandler.java index 9ef3f6f..c54d7b2 100644 --- a/src/main/java/org/onap/aai/modelloader/entity/catalog/VnfCatalogArtifactHandler.java +++ b/src/main/java/org/onap/aai/modelloader/entity/catalog/VnfCatalogArtifactHandler.java @@ -2,15 +2,14 @@ * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ - * Copyright © 2017 AT&T Intellectual Property. - * Copyright © 2017 Amdocs - * All rights reserved. + * 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 + * 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, @@ -18,176 +17,261 @@ * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= - * - * ECOMP is a trademark and service mark of AT&T Intellectual Property. */ package org.onap.aai.modelloader.entity.catalog; -import com.sun.jersey.api.client.ClientResponse; - -import generated.VnfCatalog; -import generated.VnfCatalog.PartNumberList; - -import inventory.aai.openecomp.org.v8.VnfImage; - -import org.eclipse.persistence.jaxb.MarshallerProperties; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import java.io.StringReader; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.apache.commons.text.StringEscapeUtils; +import org.apache.http.client.utils.URIBuilder; +import org.onap.aai.cl.api.Logger; +import org.onap.aai.cl.eelf.LoggerFactory; import org.onap.aai.modelloader.config.ModelLoaderConfig; import org.onap.aai.modelloader.entity.Artifact; import org.onap.aai.modelloader.entity.ArtifactHandler; import org.onap.aai.modelloader.restclient.AaiRestClient; -import org.onap.aai.modelloader.restclient.AaiRestClient.MimeType; import org.onap.aai.modelloader.service.ModelLoaderMsgs; -import org.openecomp.cl.api.Logger; -import org.openecomp.cl.eelf.LoggerFactory; -import org.springframework.web.util.UriUtils; +import org.onap.aai.restclient.client.OperationResult; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import javax.ws.rs.core.Response; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; +/** + * VNF Catalog specific handling + */ +public class VnfCatalogArtifactHandler extends ArtifactHandler { + private static Logger logger = LoggerFactory.getInstance().getLogger(VnfCatalogArtifactHandler.class.getName()); -public class VnfCatalogArtifactHandler extends ArtifactHandler { + public static final String ATTR_UUID = "uuid"; - private static Logger logger = LoggerFactory.getInstance() - .getLogger(VnfCatalogArtifactHandler.class.getName()); + public VnfCatalogArtifactHandler(ModelLoaderConfig config) { + super(config); + } - public VnfCatalogArtifactHandler(ModelLoaderConfig config) { - super(config); - } + /* + * (non-Javadoc) + * + * @see org.openecomp.modelloader.entity.ArtifactHandler#pushArtifacts(java.util.List, java.lang.String) + */ + @Override + public boolean pushArtifacts(List artifacts, String distributionId, List completedArtifacts, + AaiRestClient aaiClient) { + for (Artifact artifact : artifacts) { + try { + distributeVnfcData(aaiClient, distributionId, artifact, completedArtifacts); + } catch (VnfImageException e) { + if (e.getResultCode().isPresent()) { + logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, + "Ingestion failed on vnf-image " + e.getImageId() + " with status " + + e.getResultCode().orElse(0) + ". Rolling back distribution."); + } else { + logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, + "Ingestion failed on " + e.getImageId() + ". Rolling back distribution."); + } + return false; + } + } - @Override - public boolean pushArtifacts(List artifacts, String distributionId) { - for (Artifact art : artifacts) { - VnfCatalogArtifact vnfCatalog = (VnfCatalogArtifact) art; - String artifactPayload = vnfCatalog.getPayload(); + return true; + } - AaiRestClient restClient = new AaiRestClient(this.config); - List putImages = new ArrayList(); + /* + * If something fails in the middle of ingesting the catalog we want to roll back any changes to the DB + */ + @Override + public void rollback(List completedArtifacts, String distributionId, AaiRestClient aaiClient) { + for (Artifact completedArtifact : completedArtifacts) { + Map data = new Gson().fromJson(completedArtifact.getPayload(), + new TypeToken>() {}.getType()); + String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + data.get(ATTR_UUID); + // Try to delete the image. If something goes wrong we can't really do anything here + aaiClient.getAndDeleteResource(url, distributionId); + } + } - try { - JAXBContext inputContext = JAXBContext.newInstance(VnfCatalog.class); - Unmarshaller unmarshaller = inputContext.createUnmarshaller(); - StringReader reader = new StringReader(artifactPayload); - VnfCatalog cat = (VnfCatalog) unmarshaller.unmarshal(reader); + private void distributeVnfcData(AaiRestClient restClient, String distributionId, Artifact vnfcArtifact, + List completedArtifacts) throws VnfImageException { + List> vnfcData; + switch (vnfcArtifact.getType()) { + case VNF_CATALOG: + vnfcData = unmarshallVnfcData(vnfcArtifact); + break; + case VNF_CATALOG_XML: + vnfcData = parseXmlVnfcData(vnfcArtifact); + break; + default: + throw new VnfImageException("Unsupported type " + vnfcArtifact.getType()); + } + distributeVnfcData(restClient, distributionId, completedArtifacts, vnfcData); + } - int numParts = cat.getPartNumberList().size(); + /** + * Build a VNF image from each of the supplied data items, and distribute to AAI + * + * @param restClient + * @param distributionId + * @param completedArtifacts + * @param vnfcData + * @throws VnfImageException + */ + private void distributeVnfcData(AaiRestClient restClient, String distributionId, List completedArtifacts, + List> vnfcData) throws VnfImageException { + for (Map dataItem : vnfcData) { + // If an empty dataItem is supplied, do nothing. + if (dataItem.isEmpty()) { + logger.warn(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Empty image data supplied, skipping ingestion."); + continue; + } - for (int i = 0; i < numParts; i++) { + StringBuilder imageIdBuilder = new StringBuilder("vnf image"); + for (Entry entry : dataItem.entrySet()) { + imageIdBuilder.append(" ").append(entry.getValue()); + } + String imageId = imageIdBuilder.toString(); + int resultCode = getVnfImage(restClient, distributionId, imageId, dataItem); - PartNumberList pnl = cat.getPartNumberList().get(i); + if (resultCode == Response.Status.NOT_FOUND.getStatusCode()) { + // This vnf-image is missing, so add it + boolean success = putVnfImage(restClient, dataItem, distributionId); + if (success) { + completedArtifacts.add(new VnfCatalogArtifact(new Gson().toJson(dataItem))); + logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested."); + } else { + throw new VnfImageException(imageId); + } + } else if (resultCode == Response.Status.OK.getStatusCode()) { + logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " already exists. Skipping ingestion."); + } else { + // if other than 404 or 200, something went wrong + throw new VnfImageException(imageId, resultCode); + } + } + } - String application = pnl.getVendorInfo().getVendorModel(); - String applicationVendor = pnl.getVendorInfo().getVendorName(); + private int getVnfImage(AaiRestClient restClient, String distributionId, String imageId, + Map dataItem) throws VnfImageException { + try { + URIBuilder b = new URIBuilder(config.getAaiBaseUrl() + config.getAaiVnfImageUrl()); + for (Entry entry : dataItem.entrySet()) { + b.addParameter(entry.getKey(), entry.getValue()); + } + OperationResult tryGet = + restClient.getResource(b.build().toString(), distributionId, MediaType.APPLICATION_JSON_TYPE); + if (tryGet == null) { + throw new VnfImageException(imageId); + } + return tryGet.getResultCode(); + } catch (URISyntaxException ex) { + throw new VnfImageException(ex); + } + } - int numVersions = pnl.getSoftwareVersionList().size(); + private boolean putVnfImage(AaiRestClient restClient, Map dataItem, String distributionId) { + // Generate a new UUID for the image data item + String uuid = UUID.randomUUID().toString(); + dataItem.put(ATTR_UUID, uuid); - for (int j = 0; j < numVersions; j++) { - String applicationVersion = pnl.getSoftwareVersionList().get(j).getSoftwareVersion(); + String payload = new Gson().toJson(dataItem); + String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + uuid; + OperationResult putResp = + restClient.putResource(putUrl, payload, distributionId, MediaType.APPLICATION_JSON_TYPE); + return putResp != null && putResp.getResultCode() == Response.Status.CREATED.getStatusCode(); + } - String imageId = "vnf image " + applicationVendor + " " + application + " " - + applicationVersion; + private List> unmarshallVnfcData(Artifact vnfcArtifact) { + // Unmarshall Babel JSON payload into a List of Maps of JSON attribute name/values. + return new Gson().fromJson(StringEscapeUtils.unescapeJson(vnfcArtifact.getPayload()), + new TypeToken>>() {}.getType()); + } - String queryURI = "application-vendor=" + applicationVendor + "&application=" + application + "&application-version=" + applicationVersion; - - String getUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "?" + UriUtils.encodePath(queryURI, "UTF-8"); + /** + * Parse the VNF Catalog XML and transform into Key/Value pairs. + * + * @param vnfcArtifact + * @return VNF Image data in Map form + * @throws VnfImageException + */ + private List> parseXmlVnfcData(Artifact vnfcArtifact) throws VnfImageException { + List> vnfcData = new ArrayList<>(); + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + DocumentBuilder builder = factory.newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(vnfcArtifact.getPayload())); + Document doc = builder.parse(is); + doc.getDocumentElement().normalize(); - ClientResponse tryGet = restClient.getResource(getUrl, distributionId, MimeType.JSON); - if (tryGet == null) { - logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, - "Ingestion failed on " + imageId + ". Rolling back distribution."); - failureCleanup(putImages, restClient, distributionId); - return false; - } - if (tryGet.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) { - // this vnf-image not already in the db, need to add - // only do this on 404 bc other error responses could mean there - // are problems that - // you might not want to try to PUT against - - VnfImage image = new VnfImage(); - image.setApplication(application); - image.setApplicationVendor(applicationVendor); - image.setApplicationVersion(applicationVersion); - String uuid = UUID.randomUUID().toString(); - image.setUuid(uuid); // need to create uuid - - System.setProperty("javax.xml.bind.context.factory", - "org.eclipse.persistence.jaxb.JAXBContextFactory"); - JAXBContext jaxbContext = JAXBContext.newInstance(VnfImage.class); - Marshaller marshaller = jaxbContext.createMarshaller(); - marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json"); - marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false); - marshaller.setProperty(MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); - marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false); - StringWriter writer = new StringWriter(); - marshaller.marshal(image, writer); - String payload = writer.toString(); - - String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" - + uuid; - - ClientResponse putResp = restClient.putResource(putUrl, payload, distributionId, - MimeType.JSON); - if (putResp == null - || putResp.getStatus() != Response.Status.CREATED.getStatusCode()) { - logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, - "Ingestion failed on vnf-image " + imageId + ". Rolling back distribution."); - failureCleanup(putImages, restClient, distributionId); - return false; - } - putImages.add(image); - logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested."); - } else if (tryGet.getStatus() == Response.Status.OK.getStatusCode()) { - logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, - imageId + " already exists. Skipping ingestion."); - } else { - // if other than 404 or 200, something went wrong - logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, - "Ingestion failed on vnf-image " + imageId + " with status " + tryGet.getStatus() - + ". Rolling back distribution."); - failureCleanup(putImages, restClient, distributionId); - return false; + NodeList pnl = doc.getElementsByTagName("part-number-list"); + for (int i = 0; i < pnl.getLength(); i++) { + Node partNumber = pnl.item(i); + if (partNumber.getNodeType() == Node.ELEMENT_NODE) { + Element vendorInfo = getFirstChildNodeByName(partNumber, "vendor-info"); + if (vendorInfo != null) { + Map application = new HashMap<>(); + application.put("application", + vendorInfo.getElementsByTagName("vendor-model").item(0).getTextContent()); + application.put("application-vendor", + vendorInfo.getElementsByTagName("vendor-name").item(0).getTextContent()); + populateSoftwareVersions(vnfcData, application, partNumber); + } + } } - } + } catch (Exception ex) { + throw new VnfImageException(ex); } + return vnfcData; + } - } catch (JAXBException e) { - logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, - "Ingestion failed. " + e.getMessage() + ". Rolling back distribution."); - failureCleanup(putImages, restClient, distributionId); - return false; - } catch (UnsupportedEncodingException e) { - logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR, "Ingestion failed. " + e.getMessage() + ". Rolling back distribution."); - failureCleanup(putImages, restClient, distributionId); - return false; - } + /** + * @param vnfcData to populate + * @param applicationData + * @param partNumber + */ + private void populateSoftwareVersions(List> vnfcData, Map applicationData, + Node partNumber) { + NodeList nodes = partNumber.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node childNode = nodes.item(i); + if (childNode.getNodeName().equalsIgnoreCase("software-version-list")) { + Element softwareVersion = getFirstChildNodeByName(childNode, "software-version"); + if (softwareVersion != null) { + HashMap vnfImageData = new HashMap<>(applicationData); + vnfImageData.put("application-version", softwareVersion.getTextContent()); + vnfcData.add(vnfImageData); + } + } + } } - return true; - } - - /* - * if something fails in the middle of ingesting the catalog we want to - * rollback any changes to the db - */ - private void failureCleanup(List putImages, AaiRestClient restClient, String transId) { - for (VnfImage image : putImages) { - String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" - + image.getUuid(); - restClient.getAndDeleteResource(url, transId); // try to delete the image, - // if something goes wrong - // we can't really do - // anything here + /** + * @param node + * @param childNodeName + * @return the first child node matching the given name + */ + private Element getFirstChildNodeByName(Node node, String childNodeName) { + NodeList nodes = node.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node childNode = nodes.item(i); + if (childNode.getNodeName().equalsIgnoreCase(childNodeName)) { + return (Element) childNode; + } + } + return null; } - } }