Upgrade spring-boot to 2.7.X in model-loader
[aai/model-loader.git] / src / main / java / org / onap / aai / modelloader / notification / ArtifactDownloadManager.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 European Software Marketing Ltd.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 package org.onap.aai.modelloader.notification;
22
23 import java.time.ZonedDateTime;
24 import java.time.format.DateTimeFormatter;
25 import java.util.Base64;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.stream.Collectors;
29 import org.onap.aai.babel.service.data.BabelArtifact;
30 import org.onap.aai.babel.service.data.BabelArtifact.ArtifactType;
31 import org.onap.aai.cl.api.Logger;
32 import org.onap.aai.cl.eelf.LoggerFactory;
33 import org.onap.aai.cl.mdc.MdcContext;
34 import org.onap.aai.cl.mdc.MdcOverride;
35 import org.onap.aai.modelloader.config.ModelLoaderConfig;
36 import org.onap.aai.modelloader.entity.Artifact;
37 import org.onap.aai.modelloader.entity.model.BabelArtifactParsingException;
38 import org.onap.aai.modelloader.entity.model.IModelParser;
39 import org.onap.aai.modelloader.entity.model.NamedQueryArtifactParser;
40 import org.onap.aai.modelloader.extraction.InvalidArchiveException;
41 import org.onap.aai.modelloader.extraction.VnfCatalogExtractor;
42 import org.onap.aai.modelloader.restclient.BabelServiceClient;
43 import org.onap.aai.modelloader.restclient.BabelServiceClientException;
44 import org.onap.aai.modelloader.service.BabelServiceClientFactory;
45 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
46 import org.onap.sdc.api.IDistributionClient;
47 import org.onap.sdc.api.notification.IArtifactInfo;
48 import org.onap.sdc.api.notification.INotificationData;
49 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
50 import org.onap.sdc.utils.ArtifactTypeEnum;
51 import org.onap.sdc.utils.DistributionActionResultEnum;
52 import org.springframework.stereotype.Component;
53
54 /**
55  * This class is responsible for downloading the artifacts from the ASDC.
56  *
57  * The downloads can be TOSCA_CSAR files or VNF_CATALOG files.
58  *
59  * The status of the download is published. The status of the extraction of yml files from a TOSCA_CSAR file is also
60  * published as a deployment event.
61  *
62  * TOSCA_CSAR file artifacts will be converted into XML and returned as model artifacts.
63  */
64 @Component
65 public class ArtifactDownloadManager {
66
67     private static Logger logger = LoggerFactory.getInstance().getLogger(ArtifactDownloadManager.class);
68
69     private final IDistributionClient client;
70     private final NotificationPublisher notificationPublisher;
71     private final BabelArtifactConverter babelArtifactConverter;
72     private final ModelLoaderConfig config;
73     private final BabelServiceClientFactory clientFactory;
74     private final VnfCatalogExtractor vnfCatalogExtractor;
75
76     public ArtifactDownloadManager(IDistributionClient client, ModelLoaderConfig config,
77             BabelServiceClientFactory clientFactory, BabelArtifactConverter babelArtifactConverter, NotificationPublisher notificationPublisher, VnfCatalogExtractor vnfCatalogExtractor) {
78         this.client = client;
79         this.notificationPublisher = notificationPublisher;
80         this.babelArtifactConverter = babelArtifactConverter;
81         this.config = config;
82         this.clientFactory = clientFactory;
83         this.vnfCatalogExtractor = vnfCatalogExtractor;
84     }
85
86     /**
87      * This method downloads the artifacts from the ASDC.
88      *
89      * @param data data about the notification that is being processed
90      * @param artifacts the specific artifacts found in the data.
91      * @param modelArtifacts collection of artifacts for model query specs
92      * @param catalogArtifacts collection of artifacts that represent vnf catalog files
93      * @return boolean <code>true</code> if the download process was successful otherwise <code>false</code>
94      */
95     boolean downloadArtifacts(INotificationData data, List<IArtifactInfo> artifacts, List<Artifact> modelArtifacts,
96             List<Artifact> catalogArtifacts) {
97         boolean success = true;
98
99         for (IArtifactInfo artifact : artifacts) {
100             try {
101                 IDistributionClientDownloadResult downloadResult = downloadIndividualArtifacts(data, artifact);
102                 processDownloadedArtifacts(modelArtifacts, catalogArtifacts, artifact, downloadResult, data);
103             } catch (DownloadFailureException e) {
104                 notificationPublisher.publishDownloadFailure(client, data, artifact, e.getMessage());
105                 success = false;
106             } catch (Exception e) {
107                 notificationPublisher.publishDeployFailure(client, data, artifact);
108                 success = false;
109             }
110
111             if (!success) {
112                 break;
113             }
114         }
115
116         return success;
117     }
118
119     private IDistributionClientDownloadResult downloadIndividualArtifacts(INotificationData data,
120             IArtifactInfo artifact) throws DownloadFailureException {
121         // Grab the current time so we can measure the download time for the metrics log
122         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
123         MdcOverride override = new MdcOverride();
124         override.addAttribute(MdcContext.MDC_START_TIME, ZonedDateTime.now().format(formatter));
125
126         IDistributionClientDownloadResult downloadResult = client.download(artifact);
127
128         logger.info(ModelLoaderMsgs.DOWNLOAD_COMPLETE, downloadResult.getDistributionActionResult().toString(),
129                 downloadResult.getArtifactPayload() == null ? "null"
130                         : Base64.getEncoder().encodeToString(downloadResult.getArtifactPayload()));
131
132         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
133             logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Downloaded artifact: " + artifact.getArtifactName());
134             notificationPublisher.publishDownloadSuccess(client, data, artifact);
135         } else {
136             throw new DownloadFailureException(downloadResult.getDistributionMessageResult());
137         }
138
139         return downloadResult;
140     }
141
142     private void processDownloadedArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts,
143             IArtifactInfo artifactInfo, IDistributionClientDownloadResult downloadResult, INotificationData data)
144             throws ProcessToscaArtifactsException, InvalidArchiveException, BabelArtifactParsingException {
145         if ("TOSCA_CSAR".equalsIgnoreCase(artifactInfo.getArtifactType())) {
146             processToscaArtifacts(modelArtifacts, catalogArtifacts, downloadResult.getArtifactPayload(), artifactInfo,
147                     data.getDistributionID(), data.getServiceVersion());
148         } else if (ArtifactTypeEnum.MODEL_QUERY_SPEC.toString().equalsIgnoreCase(artifactInfo.getArtifactType())) {
149             processModelQuerySpecArtifact(modelArtifacts, downloadResult);
150         } else {
151             logger.info(ModelLoaderMsgs.UNSUPPORTED_ARTIFACT_TYPE, artifactInfo.getArtifactName(),
152                     artifactInfo.getArtifactType());
153             throw new InvalidArchiveException("Unsupported artifact type: " + artifactInfo.getArtifactType());
154         }
155     }
156
157     public void processToscaArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts, byte[] payload,
158             IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
159             throws ProcessToscaArtifactsException, InvalidArchiveException {
160         // Get translated artifacts from Babel Service
161         invokeBabelService(modelArtifacts, catalogArtifacts, payload, artifactInfo, distributionId, serviceVersion);
162
163         // Get VNF Catalog artifacts directly from CSAR
164         List<Artifact> csarCatalogArtifacts = vnfCatalogExtractor.extract(payload, artifactInfo.getArtifactName());
165
166         // Throw an error if VNF Catalog data is present in the Babel payload and directly in the CSAR
167         if (!catalogArtifacts.isEmpty() && !csarCatalogArtifacts.isEmpty()) {
168             logger.error(ModelLoaderMsgs.DUPLICATE_VNFC_DATA_ERROR, artifactInfo.getArtifactName());
169             throw new InvalidArchiveException("CSAR: " + artifactInfo.getArtifactName()
170                     + " contains VNF Catalog data in the format of both TOSCA and XML files. Only one format can be used for each CSAR file.");
171         } else if (!csarCatalogArtifacts.isEmpty()) {
172             catalogArtifacts.addAll(csarCatalogArtifacts);
173         }
174     }
175
176     public void invokeBabelService(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts, byte[] payload,
177             IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
178             throws ProcessToscaArtifactsException {
179         try {
180             BabelServiceClient babelClient = createBabelServiceClient(artifactInfo, serviceVersion);
181
182             logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT,
183                     "Posting artifact: " + artifactInfo.getArtifactName() + ", service version: " + serviceVersion
184                             + ", artifact version: " + artifactInfo.getArtifactVersion());
185
186             List<BabelArtifact> babelArtifacts =
187                     babelClient.postArtifact(payload, artifactInfo.getArtifactName(), serviceVersion, distributionId);
188
189             // Sort Babel artifacts based on type
190             Map<ArtifactType, List<BabelArtifact>> artifactMap =
191                     babelArtifacts.stream().collect(Collectors.groupingBy(BabelArtifact::getType));
192
193             if (artifactMap.containsKey(BabelArtifact.ArtifactType.MODEL)) {
194                 modelArtifacts.addAll(
195                         babelArtifactConverter.convertToModel(artifactMap.get(BabelArtifact.ArtifactType.MODEL)));
196                 artifactMap.remove(BabelArtifact.ArtifactType.MODEL);
197             }
198
199             if (artifactMap.containsKey(BabelArtifact.ArtifactType.VNFCATALOG)) {
200                 catalogArtifacts.addAll(babelArtifactConverter
201                         .convertToCatalog(artifactMap.get(BabelArtifact.ArtifactType.VNFCATALOG)));
202                 artifactMap.remove(BabelArtifact.ArtifactType.VNFCATALOG);
203             }
204
205             // Log unexpected artifact types
206             if (!artifactMap.isEmpty()) {
207                 logger.warn(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
208                         artifactInfo.getArtifactName() + " " + serviceVersion
209                                 + ". Unexpected artifact types returned by the babel service: "
210                                 + artifactMap.keySet().toString());
211             }
212
213         } catch (BabelArtifactParsingException e) {
214             logger.error(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
215                     "Error for artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + e);
216             throw new ProcessToscaArtifactsException(
217                     "An error occurred while trying to parse the Babel artifacts: " + e.getLocalizedMessage());
218         } catch (Exception e) {
219             logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
220                     "Error posting artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + " to Babel: "
221                             + e.getLocalizedMessage());
222             throw new ProcessToscaArtifactsException(
223                     "An error occurred while calling the Babel service: " + e.getLocalizedMessage());
224         }
225     }
226
227     BabelServiceClient createBabelServiceClient(IArtifactInfo artifact, String serviceVersion)
228             throws ProcessToscaArtifactsException {
229         BabelServiceClient babelClient;
230         try {
231             logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Creating Babel client");
232             babelClient = clientFactory.create(config);
233         } catch (BabelServiceClientException e) {
234             logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
235                     "Error posting artifact " + artifact.getArtifactName() + " " + serviceVersion + " to Babel: "
236                             + e.getLocalizedMessage());
237             throw new ProcessToscaArtifactsException(
238                     "An error occurred tyring to convert the tosca artifacts to xml artifacts: "
239                             + e.getLocalizedMessage());
240         }
241
242         return babelClient;
243     }
244
245     private void processModelQuerySpecArtifact(List<Artifact> modelArtifacts,
246             IDistributionClientDownloadResult downloadResult) throws BabelArtifactParsingException {
247         logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Processing named query artifact.");
248
249         IModelParser parser = new NamedQueryArtifactParser();
250
251         List<Artifact> parsedArtifacts =
252                 parser.parse(new String(downloadResult.getArtifactPayload()), downloadResult.getArtifactFilename());
253
254         if (parsedArtifactsExist(parsedArtifacts)) {
255             modelArtifacts.addAll(parsedArtifacts);
256         } else {
257             throw new BabelArtifactParsingException(
258                     "Could not parse generated XML: " + new String(downloadResult.getArtifactPayload()));
259         }
260     }
261
262     private boolean parsedArtifactsExist(List<Artifact> parsedArtifacts) {
263         return parsedArtifacts != null && !parsedArtifacts.isEmpty();
264     }
265 }