2 * ============LICENSE_START=======================================================
\r
4 * ================================================================================
\r
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
\r
6 * Copyright © 2017-2018 European Software Marketing Ltd.
\r
7 * ================================================================================
\r
8 * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * you may not use this file except in compliance with the License.
\r
10 * You may obtain a copy of the License at
\r
12 * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * Unless required by applicable law or agreed to in writing, software
\r
15 * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * See the License for the specific language governing permissions and
\r
18 * limitations under the License.
\r
19 * ============LICENSE_END=========================================================
\r
21 package org.onap.aai.modelloader.entity.catalog;
\r
23 import com.google.gson.Gson;
\r
24 import com.google.gson.reflect.TypeToken;
\r
25 import java.io.StringReader;
\r
26 import java.net.URISyntaxException;
\r
27 import java.util.ArrayList;
\r
28 import java.util.HashMap;
\r
29 import java.util.List;
\r
30 import java.util.Map;
\r
31 import java.util.Map.Entry;
\r
32 import java.util.UUID;
\r
33 import javax.xml.parsers.DocumentBuilder;
\r
34 import javax.xml.parsers.DocumentBuilderFactory;
\r
35 import org.apache.commons.text.StringEscapeUtils;
\r
36 import org.apache.http.client.utils.URIBuilder;
\r
37 import org.onap.aai.cl.api.Logger;
\r
38 import org.onap.aai.cl.eelf.LoggerFactory;
\r
39 import org.onap.aai.modelloader.config.ModelLoaderConfig;
\r
40 import org.onap.aai.modelloader.entity.Artifact;
\r
41 import org.onap.aai.modelloader.entity.ArtifactHandler;
\r
42 import org.onap.aai.modelloader.entity.vnf.VnfImages;
\r
43 import org.onap.aai.modelloader.restclient.AaiRestClient;
\r
44 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
\r
45 import org.springframework.http.HttpStatus;
\r
46 import org.springframework.http.MediaType;
\r
47 import org.springframework.http.ResponseEntity;
\r
48 import org.springframework.stereotype.Component;
\r
49 import org.springframework.web.client.RestTemplate;
\r
50 import org.w3c.dom.Document;
\r
51 import org.w3c.dom.Element;
\r
52 import org.w3c.dom.Node;
\r
53 import org.w3c.dom.NodeList;
\r
54 import org.xml.sax.InputSource;
\r
57 * VNF Catalog specific handling
\r
60 public class VnfCatalogArtifactHandler extends ArtifactHandler {
\r
62 private static Logger logger = LoggerFactory.getInstance().getLogger(VnfCatalogArtifactHandler.class.getName());
\r
64 public static final String ATTR_UUID = "uuid";
\r
66 public VnfCatalogArtifactHandler(ModelLoaderConfig config) {
\r
73 * @see org.openecomp.modelloader.entity.ArtifactHandler#pushArtifacts(java.util.List, java.lang.String)
\r
76 public boolean pushArtifacts(List<Artifact> artifacts, String distributionId, List<Artifact> completedArtifacts,
\r
77 AaiRestClient aaiClient) {
\r
78 for (Artifact artifact : artifacts) {
\r
80 distributeVnfcData(aaiClient, distributionId, artifact, completedArtifacts);
\r
81 } catch (VnfImageException e) {
\r
82 if (e.getResultCode().isPresent()) {
\r
83 logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,
\r
84 "Ingestion failed on vnf-image " + e.getImageId() + " with status "
\r
85 + e.getResultCode().orElse(0) + ". Rolling back distribution.");
\r
87 logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,
\r
88 "Ingestion failed on " + e.getImageId() + ". Rolling back distribution.");
\r
98 * If something fails in the middle of ingesting the catalog we want to roll back any changes to the DB
\r
101 public void rollback(List<Artifact> completedArtifacts, String distributionId, AaiRestClient aaiClient) {
\r
102 for (Artifact completedArtifact : completedArtifacts) {
\r
103 Map<String, String> data = new Gson().fromJson(completedArtifact.getPayload(),
\r
104 new TypeToken<Map<String, String>>() {}.getType());
\r
105 String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + data.get(ATTR_UUID);
\r
106 // Try to delete the image. If something goes wrong we can't really do anything here
\r
107 aaiClient.getAndDeleteResource(url, distributionId);
\r
111 private void distributeVnfcData(AaiRestClient restClient, String distributionId, Artifact vnfcArtifact,
\r
112 List<Artifact> completedArtifacts) throws VnfImageException {
\r
113 List<Map<String, String>> vnfcData;
\r
114 switch (vnfcArtifact.getType()) {
\r
116 vnfcData = unmarshallVnfcData(vnfcArtifact);
\r
118 case VNF_CATALOG_XML:
\r
119 vnfcData = parseXmlVnfcData(vnfcArtifact);
\r
122 throw new VnfImageException("Unsupported type " + vnfcArtifact.getType());
\r
124 distributeVnfcData(restClient, distributionId, completedArtifacts, vnfcData);
\r
128 * Build a VNF image from each of the supplied data items, and distribute to AAI
\r
130 * @param restClient
\r
131 * @param distributionId
\r
132 * @param completedArtifacts
\r
134 * @throws VnfImageException
\r
136 private void distributeVnfcData(AaiRestClient restClient, String distributionId, List<Artifact> completedArtifacts,
\r
137 List<Map<String, String>> vnfcData) throws VnfImageException {
\r
138 for (Map<String, String> dataItem : vnfcData) {
\r
139 // If an empty dataItem is supplied, do nothing.
\r
140 if (dataItem.isEmpty()) {
\r
141 logger.warn(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Empty image data supplied, skipping ingestion.");
\r
145 StringBuilder imageIdBuilder = new StringBuilder("vnf image");
\r
146 for (Entry<String, String> entry : dataItem.entrySet()) {
\r
147 imageIdBuilder.append(" ").append(entry.getValue());
\r
149 String imageId = imageIdBuilder.toString();
\r
150 int resultCode = getVnfImage(restClient, distributionId, imageId, dataItem);
\r
152 if (resultCode == HttpStatus.NOT_FOUND.value()) {
\r
153 // This vnf-image is missing, so add it
\r
154 boolean success = putVnfImage(restClient, dataItem, distributionId);
\r
156 completedArtifacts.add(new VnfCatalogArtifact(new Gson().toJson(dataItem)));
\r
157 logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested.");
\r
159 throw new VnfImageException(imageId);
\r
161 } else if (resultCode == HttpStatus.OK.value()) {
\r
162 logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " already exists. Skipping ingestion.");
\r
164 // if other than 404 or 200, something went wrong
\r
165 throw new VnfImageException(imageId, resultCode);
\r
170 private int getVnfImage(AaiRestClient restClient, String distributionId, String imageId,
\r
171 Map<String, String> dataItem) throws VnfImageException {
\r
173 URIBuilder b = new URIBuilder(config.getAaiBaseUrl() + config.getAaiVnfImageUrl());
\r
174 for (Entry<String, String> entry : dataItem.entrySet()) {
\r
175 b.addParameter(entry.getKey(), entry.getValue());
\r
177 ResponseEntity<VnfImages> tryGet =
\r
178 restClient.getResource(b.build().toString(), distributionId, MediaType.APPLICATION_JSON, VnfImages.class);
\r
179 if (tryGet == null) {
\r
180 throw new VnfImageException(imageId);
\r
182 return tryGet.getStatusCodeValue();
\r
183 } catch (URISyntaxException ex) {
\r
184 throw new VnfImageException(ex);
\r
188 private boolean putVnfImage(AaiRestClient restClient, Map<String, String> dataItem, String distributionId) {
\r
189 // Generate a new UUID for the image data item
\r
190 String uuid = UUID.randomUUID().toString();
\r
191 dataItem.put(ATTR_UUID, uuid);
\r
193 // TODO: Get rid of the dataItem map and replace it with the VnfImage object
\r
194 String payload = new Gson().toJson(dataItem);
\r
195 String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + uuid;
\r
196 ResponseEntity<String> putResp =
\r
197 restClient.putResource(putUrl, payload, distributionId, MediaType.APPLICATION_JSON, String.class);
\r
198 return putResp != null && putResp.getStatusCode() == HttpStatus.CREATED;
\r
201 private List<Map<String, String>> unmarshallVnfcData(Artifact vnfcArtifact) {
\r
202 // Unmarshall Babel JSON payload into a List of Maps of JSON attribute name/values.
\r
203 return new Gson().fromJson(StringEscapeUtils.unescapeJson(vnfcArtifact.getPayload()),
\r
204 new TypeToken<List<Map<String, String>>>() {}.getType());
\r
208 * Parse the VNF Catalog XML and transform into Key/Value pairs.
\r
210 * @param vnfcArtifact
\r
211 * @return VNF Image data in Map form
\r
212 * @throws VnfImageException
\r
214 private List<Map<String, String>> parseXmlVnfcData(Artifact vnfcArtifact) throws VnfImageException {
\r
215 List<Map<String, String>> vnfcData = new ArrayList<>();
\r
217 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
\r
218 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
\r
219 DocumentBuilder builder = factory.newDocumentBuilder();
\r
220 InputSource is = new InputSource(new StringReader(vnfcArtifact.getPayload()));
\r
221 Document doc = builder.parse(is);
\r
222 doc.getDocumentElement().normalize();
\r
224 NodeList pnl = doc.getElementsByTagName("part-number-list");
\r
225 for (int i = 0; i < pnl.getLength(); i++) {
\r
226 Node partNumber = pnl.item(i);
\r
227 if (partNumber.getNodeType() == Node.ELEMENT_NODE) {
\r
228 Element vendorInfo = getFirstChildNodeByName(partNumber, "vendor-info");
\r
229 if (vendorInfo != null) {
\r
230 Map<String, String> application = new HashMap<>();
\r
231 application.put("application",
\r
232 vendorInfo.getElementsByTagName("vendor-model").item(0).getTextContent());
\r
233 application.put("application-vendor",
\r
234 vendorInfo.getElementsByTagName("vendor-name").item(0).getTextContent());
\r
235 populateSoftwareVersions(vnfcData, application, partNumber);
\r
239 } catch (Exception ex) {
\r
240 throw new VnfImageException(ex);
\r
246 * @param vnfcData to populate
\r
247 * @param applicationData
\r
248 * @param partNumber
\r
250 private void populateSoftwareVersions(List<Map<String, String>> vnfcData, Map<String, String> applicationData,
\r
252 NodeList nodes = partNumber.getChildNodes();
\r
253 for (int i = 0; i < nodes.getLength(); i++) {
\r
254 Node childNode = nodes.item(i);
\r
255 if (childNode.getNodeName().equalsIgnoreCase("software-version-list")) {
\r
256 Element softwareVersion = getFirstChildNodeByName(childNode, "software-version");
\r
257 if (softwareVersion != null) {
\r
258 HashMap<String, String> vnfImageData = new HashMap<>(applicationData);
\r
259 vnfImageData.put("application-version", softwareVersion.getTextContent());
\r
260 vnfcData.add(vnfImageData);
\r
268 * @param childNodeName
\r
269 * @return the first child node matching the given name
\r
271 private Element getFirstChildNodeByName(Node node, String childNodeName) {
\r
272 NodeList nodes = node.getChildNodes();
\r
273 for (int i = 0; i < nodes.getLength(); i++) {
\r
274 Node childNode = nodes.item(i);
\r
275 if (childNode.getNodeName().equalsIgnoreCase(childNodeName)) {
\r
276 return (Element) childNode;
\r