Upgrade spring-boot to 2.7.X in model-loader
[aai/model-loader.git] / src / main / java / org / onap / aai / modelloader / entity / catalog / VnfCatalogArtifactHandler.java
1 /**\r
2  * ============LICENSE_START=======================================================\r
3  * org.onap.aai\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
11  *\r
12  *       http://www.apache.org/licenses/LICENSE-2.0\r
13  *\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
20  */\r
21 package org.onap.aai.modelloader.entity.catalog;\r
22 \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.ws.rs.core.MediaType;\r
34 import javax.ws.rs.core.Response;\r
35 import javax.xml.parsers.DocumentBuilder;\r
36 import javax.xml.parsers.DocumentBuilderFactory;\r
37 import org.apache.commons.text.StringEscapeUtils;\r
38 import org.apache.http.client.utils.URIBuilder;\r
39 import org.onap.aai.cl.api.Logger;\r
40 import org.onap.aai.cl.eelf.LoggerFactory;\r
41 import org.onap.aai.modelloader.config.ModelLoaderConfig;\r
42 import org.onap.aai.modelloader.entity.Artifact;\r
43 import org.onap.aai.modelloader.entity.ArtifactHandler;\r
44 import org.onap.aai.modelloader.restclient.AaiRestClient;\r
45 import org.onap.aai.modelloader.service.ModelLoaderMsgs;\r
46 import org.onap.aai.restclient.client.OperationResult;\r
47 import org.springframework.stereotype.Component;\r
48 import org.w3c.dom.Document;\r
49 import org.w3c.dom.Element;\r
50 import org.w3c.dom.Node;\r
51 import org.w3c.dom.NodeList;\r
52 import org.xml.sax.InputSource;\r
53 \r
54 /**\r
55  * VNF Catalog specific handling\r
56  */\r
57 @Component\r
58 public class VnfCatalogArtifactHandler extends ArtifactHandler {\r
59 \r
60     private static Logger logger = LoggerFactory.getInstance().getLogger(VnfCatalogArtifactHandler.class.getName());\r
61 \r
62     public static final String ATTR_UUID = "uuid";\r
63 \r
64     public VnfCatalogArtifactHandler(ModelLoaderConfig config) {\r
65         super(config);\r
66     }\r
67 \r
68     /*\r
69      * (non-Javadoc)\r
70      * \r
71      * @see org.openecomp.modelloader.entity.ArtifactHandler#pushArtifacts(java.util.List, java.lang.String)\r
72      */\r
73     @Override\r
74     public boolean pushArtifacts(List<Artifact> artifacts, String distributionId, List<Artifact> completedArtifacts,\r
75             AaiRestClient aaiClient) {\r
76         for (Artifact artifact : artifacts) {\r
77             try {\r
78                 distributeVnfcData(aaiClient, distributionId, artifact, completedArtifacts);\r
79             } catch (VnfImageException e) {\r
80                 if (e.getResultCode().isPresent()) {\r
81                     logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
82                             "Ingestion failed on vnf-image " + e.getImageId() + " with status "\r
83                                     + e.getResultCode().orElse(0) + ". Rolling back distribution.");\r
84                 } else {\r
85                     logger.error(ModelLoaderMsgs.DISTRIBUTION_EVENT_ERROR,\r
86                             "Ingestion failed on " + e.getImageId() + ". Rolling back distribution.");\r
87                 }\r
88                 return false;\r
89             }\r
90         }\r
91 \r
92         return true;\r
93     }\r
94 \r
95     /*\r
96      * If something fails in the middle of ingesting the catalog we want to roll back any changes to the DB\r
97      */\r
98     @Override\r
99     public void rollback(List<Artifact> completedArtifacts, String distributionId, AaiRestClient aaiClient) {\r
100         for (Artifact completedArtifact : completedArtifacts) {\r
101             Map<String, String> data = new Gson().fromJson(completedArtifact.getPayload(),\r
102                     new TypeToken<Map<String, String>>() {}.getType());\r
103             String url = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + data.get(ATTR_UUID);\r
104             // Try to delete the image. If something goes wrong we can't really do anything here\r
105             aaiClient.getAndDeleteResource(url, distributionId);\r
106         }\r
107     }\r
108 \r
109     private void distributeVnfcData(AaiRestClient restClient, String distributionId, Artifact vnfcArtifact,\r
110             List<Artifact> completedArtifacts) throws VnfImageException {\r
111         List<Map<String, String>> vnfcData;\r
112         switch (vnfcArtifact.getType()) {\r
113             case VNF_CATALOG:\r
114                 vnfcData = unmarshallVnfcData(vnfcArtifact);\r
115                 break;\r
116             case VNF_CATALOG_XML:\r
117                 vnfcData = parseXmlVnfcData(vnfcArtifact);\r
118                 break;\r
119             default:\r
120                 throw new VnfImageException("Unsupported type " + vnfcArtifact.getType());\r
121         }\r
122         distributeVnfcData(restClient, distributionId, completedArtifacts, vnfcData);\r
123     }\r
124 \r
125     /**\r
126      * Build a VNF image from each of the supplied data items, and distribute to AAI\r
127      * \r
128      * @param restClient\r
129      * @param distributionId\r
130      * @param completedArtifacts\r
131      * @param vnfcData\r
132      * @throws VnfImageException\r
133      */\r
134     private void distributeVnfcData(AaiRestClient restClient, String distributionId, List<Artifact> completedArtifacts,\r
135             List<Map<String, String>> vnfcData) throws VnfImageException {\r
136         for (Map<String, String> dataItem : vnfcData) {\r
137             // If an empty dataItem is supplied, do nothing.\r
138             if (dataItem.isEmpty()) {\r
139                 logger.warn(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Empty image data supplied, skipping ingestion.");\r
140                 continue;\r
141             }\r
142 \r
143             StringBuilder imageIdBuilder = new StringBuilder("vnf image");\r
144             for (Entry<String, String> entry : dataItem.entrySet()) {\r
145                 imageIdBuilder.append(" ").append(entry.getValue());\r
146             }\r
147             String imageId = imageIdBuilder.toString();\r
148             int resultCode = getVnfImage(restClient, distributionId, imageId, dataItem);\r
149 \r
150             if (resultCode == Response.Status.NOT_FOUND.getStatusCode()) {\r
151                 // This vnf-image is missing, so add it\r
152                 boolean success = putVnfImage(restClient, dataItem, distributionId);\r
153                 if (success) {\r
154                     completedArtifacts.add(new VnfCatalogArtifact(new Gson().toJson(dataItem)));\r
155                     logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " successfully ingested.");\r
156                 } else {\r
157                     throw new VnfImageException(imageId);\r
158                 }\r
159             } else if (resultCode == Response.Status.OK.getStatusCode()) {\r
160                 logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, imageId + " already exists. Skipping ingestion.");\r
161             } else {\r
162                 // if other than 404 or 200, something went wrong\r
163                 throw new VnfImageException(imageId, resultCode);\r
164             }\r
165         }\r
166     }\r
167 \r
168     private int getVnfImage(AaiRestClient restClient, String distributionId, String imageId,\r
169             Map<String, String> dataItem) throws VnfImageException {\r
170         try {\r
171             URIBuilder b = new URIBuilder(config.getAaiBaseUrl() + config.getAaiVnfImageUrl());\r
172             for (Entry<String, String> entry : dataItem.entrySet()) {\r
173                 b.addParameter(entry.getKey(), entry.getValue());\r
174             }\r
175             OperationResult tryGet =\r
176                     restClient.getResource(b.build().toString(), distributionId, MediaType.APPLICATION_JSON_TYPE);\r
177             if (tryGet == null) {\r
178                 throw new VnfImageException(imageId);\r
179             }\r
180             return tryGet.getResultCode();\r
181         } catch (URISyntaxException ex) {\r
182             throw new VnfImageException(ex);\r
183         }\r
184     }\r
185 \r
186     private boolean putVnfImage(AaiRestClient restClient, Map<String, String> dataItem, String distributionId) {\r
187         // Generate a new UUID for the image data item\r
188         String uuid = UUID.randomUUID().toString();\r
189         dataItem.put(ATTR_UUID, uuid);\r
190 \r
191         String payload = new Gson().toJson(dataItem);\r
192         String putUrl = config.getAaiBaseUrl() + config.getAaiVnfImageUrl() + "/vnf-image/" + uuid;\r
193         OperationResult putResp =\r
194                 restClient.putResource(putUrl, payload, distributionId, MediaType.APPLICATION_JSON_TYPE);\r
195         return putResp != null && putResp.getResultCode() == Response.Status.CREATED.getStatusCode();\r
196     }\r
197 \r
198     private List<Map<String, String>> unmarshallVnfcData(Artifact vnfcArtifact) {\r
199         // Unmarshall Babel JSON payload into a List of Maps of JSON attribute name/values.\r
200         return new Gson().fromJson(StringEscapeUtils.unescapeJson(vnfcArtifact.getPayload()),\r
201                 new TypeToken<List<Map<String, String>>>() {}.getType());\r
202     }\r
203 \r
204     /**\r
205      * Parse the VNF Catalog XML and transform into Key/Value pairs.\r
206      * \r
207      * @param vnfcArtifact\r
208      * @return VNF Image data in Map form\r
209      * @throws VnfImageException\r
210      */\r
211     private List<Map<String, String>> parseXmlVnfcData(Artifact vnfcArtifact) throws VnfImageException {\r
212         List<Map<String, String>> vnfcData = new ArrayList<>();\r
213         try {\r
214             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();\r
215             factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);\r
216             DocumentBuilder builder = factory.newDocumentBuilder();\r
217             InputSource is = new InputSource(new StringReader(vnfcArtifact.getPayload()));\r
218             Document doc = builder.parse(is);\r
219             doc.getDocumentElement().normalize();\r
220 \r
221             NodeList pnl = doc.getElementsByTagName("part-number-list");\r
222             for (int i = 0; i < pnl.getLength(); i++) {\r
223                 Node partNumber = pnl.item(i);\r
224                 if (partNumber.getNodeType() == Node.ELEMENT_NODE) {\r
225                     Element vendorInfo = getFirstChildNodeByName(partNumber, "vendor-info");\r
226                     if (vendorInfo != null) {\r
227                         Map<String, String> application = new HashMap<>();\r
228                         application.put("application",\r
229                                 vendorInfo.getElementsByTagName("vendor-model").item(0).getTextContent());\r
230                         application.put("application-vendor",\r
231                                 vendorInfo.getElementsByTagName("vendor-name").item(0).getTextContent());\r
232                         populateSoftwareVersions(vnfcData, application, partNumber);\r
233                     }\r
234                 }\r
235             }\r
236         } catch (Exception ex) {\r
237             throw new VnfImageException(ex);\r
238         }\r
239         return vnfcData;\r
240     }\r
241 \r
242     /**\r
243      * @param vnfcData to populate\r
244      * @param applicationData\r
245      * @param partNumber\r
246      */\r
247     private void populateSoftwareVersions(List<Map<String, String>> vnfcData, Map<String, String> applicationData,\r
248             Node partNumber) {\r
249         NodeList nodes = partNumber.getChildNodes();\r
250         for (int i = 0; i < nodes.getLength(); i++) {\r
251             Node childNode = nodes.item(i);\r
252             if (childNode.getNodeName().equalsIgnoreCase("software-version-list")) {\r
253                 Element softwareVersion = getFirstChildNodeByName(childNode, "software-version");\r
254                 if (softwareVersion != null) {\r
255                     HashMap<String, String> vnfImageData = new HashMap<>(applicationData);\r
256                     vnfImageData.put("application-version", softwareVersion.getTextContent());\r
257                     vnfcData.add(vnfImageData);\r
258                 }\r
259             }\r
260         }\r
261     }\r
262 \r
263     /**\r
264      * @param node\r
265      * @param childNodeName\r
266      * @return the first child node matching the given name\r
267      */\r
268     private Element getFirstChildNodeByName(Node node, String childNodeName) {\r
269         NodeList nodes = node.getChildNodes();\r
270         for (int i = 0; i < nodes.getLength(); i++) {\r
271             Node childNode = nodes.item(i);\r
272             if (childNode.getNodeName().equalsIgnoreCase(childNodeName)) {\r
273                 return (Element) childNode;\r
274             }\r
275         }\r
276         return null;\r
277     }\r
278 \r
279 }\r