Stop sdc-distribution-client upon service shutdown
[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.stream.Collectors;
28 import java.util.stream.Stream;
29
30 import org.onap.aai.babel.service.data.BabelRequest;
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.babel.BabelArtifactService;
36 import org.onap.aai.modelloader.entity.Artifact;
37 import org.onap.aai.modelloader.entity.ArtifactType;
38 import org.onap.aai.modelloader.entity.catalog.VnfCatalogArtifact;
39 import org.onap.aai.modelloader.entity.model.BabelArtifactParsingException;
40 import org.onap.aai.modelloader.entity.model.IModelParser;
41 import org.onap.aai.modelloader.entity.model.NamedQueryArtifactParser;
42 import org.onap.aai.modelloader.extraction.InvalidArchiveException;
43 import org.onap.aai.modelloader.extraction.VnfCatalogExtractor;
44 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
45 import org.onap.sdc.api.IDistributionClient;
46 import org.onap.sdc.api.notification.IArtifactInfo;
47 import org.onap.sdc.api.notification.INotificationData;
48 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
49 import org.onap.sdc.utils.ArtifactTypeEnum;
50 import org.onap.sdc.utils.DistributionActionResultEnum;
51 import org.springframework.stereotype.Component;
52
53 /**
54  * This class is responsible for downloading the artifacts from the ASDC.
55  *
56  * The downloads can be TOSCA_CSAR files or VNF_CATALOG files.
57  *
58  * The status of the download is published. The status of the extraction of yml files from a TOSCA_CSAR file is also
59  * published as a deployment event.
60  *
61  * TOSCA_CSAR file artifacts will be converted into XML and returned as model artifacts.
62  */
63 @Component
64 public class ArtifactDownloadManager {
65
66     private static Logger logger = LoggerFactory.getInstance().getLogger(ArtifactDownloadManager.class);
67
68     private final IDistributionClient client;
69     private final NotificationPublisher notificationPublisher;
70     private final VnfCatalogExtractor vnfCatalogExtractor;
71     private final BabelArtifactService babelArtifactService;
72
73     public ArtifactDownloadManager(IDistributionClient client,
74             NotificationPublisher notificationPublisher, VnfCatalogExtractor vnfCatalogExtractor, BabelArtifactService babelArtifactService) {
75         this.client = client;
76         this.notificationPublisher = notificationPublisher;
77         this.vnfCatalogExtractor = vnfCatalogExtractor;
78         this.babelArtifactService = babelArtifactService;
79     }
80
81     /**
82      * This method downloads the artifacts from the ASDC.
83      *
84      * @param data data about the notification that is being processed
85      * @param artifacts the specific artifacts found in the data.
86      * @param modelArtifacts collection of artifacts for model query specs
87      * @param catalogArtifacts collection of artifacts that represent vnf catalog files
88      * @return boolean <code>true</code> if the download process was successful otherwise <code>false</code>
89      */
90     boolean downloadArtifacts(INotificationData data, List<IArtifactInfo> artifacts, List<Artifact> modelArtifacts,
91             List<Artifact> catalogArtifacts) {
92         boolean success = true;
93
94         for (IArtifactInfo artifact : artifacts) {
95             try {
96                 IDistributionClientDownloadResult downloadResult = downloadIndividualArtifacts(data, artifact);
97                 processDownloadedArtifacts(modelArtifacts, catalogArtifacts, artifact, downloadResult, data);
98             } catch (DownloadFailureException e) {
99                 notificationPublisher.publishDownloadFailure(client, data, artifact, e.getMessage());
100                 success = false;
101             } catch (Exception e) {
102                 notificationPublisher.publishDeployFailure(client, data, artifact);
103                 success = false;
104             }
105
106             if (!success) {
107                 break;
108             }
109         }
110
111         return success;
112     }
113
114     private IDistributionClientDownloadResult downloadIndividualArtifacts(INotificationData data,
115             IArtifactInfo artifact) throws DownloadFailureException {
116         // Grab the current time so we can measure the download time for the metrics log
117         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
118         MdcOverride override = new MdcOverride();
119         override.addAttribute(MdcContext.MDC_START_TIME, ZonedDateTime.now().format(formatter));
120
121         IDistributionClientDownloadResult downloadResult = client.download(artifact);
122
123         logger.info(ModelLoaderMsgs.DOWNLOAD_COMPLETE, downloadResult.getDistributionActionResult().toString(),
124                 downloadResult.getArtifactPayload() == null ? "null"
125                         : Base64.getEncoder().encodeToString(downloadResult.getArtifactPayload()));
126
127         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
128             logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Downloaded artifact: " + artifact.getArtifactName());
129             notificationPublisher.publishDownloadSuccess(client, data, artifact);
130         } else {
131             throw new DownloadFailureException(downloadResult.getDistributionMessageResult());
132         }
133
134         return downloadResult;
135     }
136
137     private void processDownloadedArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts,
138             IArtifactInfo artifactInfo, IDistributionClientDownloadResult downloadResult, INotificationData data)
139             throws ProcessToscaArtifactsException, InvalidArchiveException, BabelArtifactParsingException {
140         List<Artifact> artifacts = null;
141         if ("TOSCA_CSAR".equalsIgnoreCase(artifactInfo.getArtifactType())) {
142             artifacts = processToscaArtifacts(downloadResult.getArtifactPayload(), artifactInfo,
143                     data.getDistributionID(), data.getServiceVersion());
144             
145         } else if (ArtifactTypeEnum.MODEL_QUERY_SPEC.toString().equalsIgnoreCase(artifactInfo.getArtifactType())) {
146             processModelQuerySpecArtifact(modelArtifacts, downloadResult);
147         } else {
148             logger.info(ModelLoaderMsgs.UNSUPPORTED_ARTIFACT_TYPE, artifactInfo.getArtifactName(),
149                     artifactInfo.getArtifactType());
150             throw new InvalidArchiveException("Unsupported artifact type: " + artifactInfo.getArtifactType());
151         }
152         if(artifacts != null) {
153             for(Artifact artifact : artifacts) {
154                 if(artifact.getType() == ArtifactType.VNF_CATALOG || artifact.getType() == ArtifactType.VNF_CATALOG_XML) {
155                     catalogArtifacts.add(artifact);
156                 } else {
157                     modelArtifacts.add(artifact);
158                 }
159             }
160         }
161     }
162
163     public List<Artifact> processToscaArtifacts(byte[] payload, IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
164             throws ProcessToscaArtifactsException, InvalidArchiveException {
165         // Get translated artifacts from Babel Service
166         BabelRequest babelRequest = new BabelRequest();
167         babelRequest.setArtifactName(artifactInfo.getArtifactName());
168         babelRequest.setCsar(Base64.getEncoder().encodeToString(payload));
169         babelRequest.setArtifactVersion(serviceVersion);
170         List<Artifact> artifacts = babelArtifactService.invokeBabelService(babelRequest, distributionId);
171
172         // Get VNF Catalog artifacts directly from CSAR
173         List<Artifact> csarCatalogArtifacts = vnfCatalogExtractor.extract(payload, artifactInfo.getArtifactName());
174
175         // Throw an error if VNF Catalog data is present in the Babel payload and directly in the CSAR
176         if (isDuplicateVnfCatalogData(artifacts, csarCatalogArtifacts)) {
177             logger.error(ModelLoaderMsgs.DUPLICATE_VNFC_DATA_ERROR, artifactInfo.getArtifactName());
178             throw new InvalidArchiveException("CSAR: " + artifactInfo.getArtifactName()
179                     + " contains VNF Catalog data in the format of both TOSCA and XML files. Only one format can be used for each CSAR file.");
180         }
181         return Stream
182                 .concat(artifacts.stream(), csarCatalogArtifacts.stream())
183                 .collect(Collectors.toList());
184
185     }
186
187     private boolean isDuplicateVnfCatalogData(List<Artifact> babelArtifacts, List<Artifact> csarCatalogArtifacts) {
188         boolean babelIsEmpty = babelArtifacts.stream()
189             .filter(VnfCatalogArtifact.class::isInstance)
190             .findAny().isEmpty();
191         return !csarCatalogArtifacts.isEmpty() && !babelIsEmpty;
192     }
193
194     private void processModelQuerySpecArtifact(List<Artifact> modelArtifacts,
195             IDistributionClientDownloadResult downloadResult) throws BabelArtifactParsingException {
196         logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Processing named query artifact.");
197
198         IModelParser parser = new NamedQueryArtifactParser();
199
200         List<Artifact> parsedArtifacts =
201                 parser.parse(new String(downloadResult.getArtifactPayload()), downloadResult.getArtifactFilename());
202
203         if (parsedArtifactsExist(parsedArtifacts)) {
204             modelArtifacts.addAll(parsedArtifacts);
205         } else {
206             throw new BabelArtifactParsingException(
207                     "Could not parse generated XML: " + new String(downloadResult.getArtifactPayload()));
208         }
209     }
210
211     private boolean parsedArtifactsExist(List<Artifact> parsedArtifacts) {
212         return parsedArtifacts != null && !parsedArtifacts.isEmpty();
213     }
214 }