3fa0b409358c84d2f849edab257a6a6a92149901
[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 Amdocs
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.restclient.BabelServiceClient;
42 import org.onap.aai.modelloader.restclient.BabelServiceClientFactory;
43 import org.onap.aai.modelloader.service.ModelLoaderMsgs;
44 import org.openecomp.sdc.api.IDistributionClient;
45 import org.openecomp.sdc.api.notification.IArtifactInfo;
46 import org.openecomp.sdc.api.notification.INotificationData;
47 import org.openecomp.sdc.api.results.IDistributionClientDownloadResult;
48 import org.openecomp.sdc.utils.ArtifactTypeEnum;
49 import org.openecomp.sdc.utils.DistributionActionResultEnum;
50
51 /**
52  * This class is responsible for downloading the artifacts from the ASDC.
53  *
54  * The downloads can be TOSCA_CSAR files or VNF_CATALOG files.
55  *
56  * The status of the download is published. The status of the extraction of yml files from a TOSCA_CSAR file is also
57  * published as a deployment event.
58  *
59  * TOSCA_CSAR file artifacts will be converted into XML and returned as model artifacts.
60  */
61 public class ArtifactDownloadManager {
62
63     private static Logger logger = LoggerFactory.getInstance().getLogger(ArtifactDownloadManager.class);
64
65     private IDistributionClient client;
66     private NotificationPublisher notificationPublisher;
67     private BabelArtifactConverter babelArtifactConverter;
68     private ModelLoaderConfig config;
69     private BabelServiceClientFactory clientFactory;
70
71     public ArtifactDownloadManager(IDistributionClient client, ModelLoaderConfig config,
72             BabelServiceClientFactory clientFactory) {
73         this.client = client;
74         this.config = config;
75         this.clientFactory = clientFactory;
76     }
77
78     /**
79      * This method downloads the artifacts from the ASDC.
80      *
81      * @param data data about the notification that is being processed
82      * @param artifacts the specific artifacts found in the data.
83      * @param modelArtifacts collection of artifacts for model query specs
84      * @param catalogArtifacts collection of artifacts that represent vnf catalog files
85      * @return boolean <code>true</code> if the download process was successful otherwise <code>false</code>
86      */
87     boolean downloadArtifacts(INotificationData data, List<IArtifactInfo> artifacts, List<Artifact> modelArtifacts,
88             List<Artifact> catalogArtifacts) {
89         boolean success = true;
90
91         for (IArtifactInfo artifact : artifacts) {
92             try {
93                 IDistributionClientDownloadResult downloadResult = downloadIndividualArtifacts(data, artifact);
94                 processDownloadedArtifacts(modelArtifacts, catalogArtifacts, artifact, downloadResult, data);
95             } catch (DownloadFailureException e) {
96                 getNotificationPublisher().publishDownloadFailure(client, data, artifact, e.getMessage());
97                 success = false;
98             } catch (Exception e) {
99                 getNotificationPublisher().publishDeployFailure(client, data, artifact);
100                 success = false;
101             }
102
103             if (!success) {
104                 break;
105             }
106         }
107
108         return success;
109     }
110
111     private IDistributionClientDownloadResult downloadIndividualArtifacts(INotificationData data,
112             IArtifactInfo artifact) throws DownloadFailureException {
113         // Grab the current time so we can measure the download time for the metrics log
114         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
115         MdcOverride override = new MdcOverride();
116         override.addAttribute(MdcContext.MDC_START_TIME, ZonedDateTime.now().format(formatter));
117
118         IDistributionClientDownloadResult downloadResult = client.download(artifact);
119
120         logger.info(ModelLoaderMsgs.DOWNLOAD_COMPLETE, downloadResult.getDistributionActionResult().toString(),
121                 downloadResult.getArtifactPayload() == null ? "null"
122                         : Base64.getEncoder().encodeToString(downloadResult.getArtifactPayload()));
123
124         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
125             logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Downloaded artifact: " + artifact.getArtifactName());
126             getNotificationPublisher().publishDownloadSuccess(client, data, artifact);
127         } else {
128             throw new DownloadFailureException(downloadResult.getDistributionMessageResult());
129         }
130
131         return downloadResult;
132     }
133
134     private void processDownloadedArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts,
135             IArtifactInfo artifactInfo, IDistributionClientDownloadResult downloadResult, INotificationData data)
136             throws ProcessToscaArtifactsException, InvalidArchiveException, BabelArtifactParsingException {
137         if ("TOSCA_CSAR".equalsIgnoreCase(artifactInfo.getArtifactType())) {
138             processToscaArtifacts(modelArtifacts, catalogArtifacts, downloadResult.getArtifactPayload(), artifactInfo,
139                     data.getDistributionID(), data.getServiceVersion());
140         } else if (ArtifactTypeEnum.MODEL_QUERY_SPEC.toString().equalsIgnoreCase(artifactInfo.getArtifactType())) {
141             processModelQuerySpecArtifact(modelArtifacts, downloadResult);
142         } else {
143             logger.info(ModelLoaderMsgs.UNSUPPORTED_ARTIFACT_TYPE, artifactInfo.getArtifactName(),
144                     artifactInfo.getArtifactType());
145             throw new InvalidArchiveException("Unsupported artifact type: " + artifactInfo.getArtifactType());
146         }
147     }
148
149     public void processToscaArtifacts(List<Artifact> modelArtifacts, List<Artifact> catalogArtifacts, byte[] payload,
150             IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
151             throws ProcessToscaArtifactsException {
152         try {
153             BabelServiceClient babelClient = createBabelServiceClient(artifactInfo, serviceVersion);
154
155             logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT,
156                     "Posting artifact: " + artifactInfo.getArtifactName() + ", version: " + serviceVersion);
157
158             List<BabelArtifact> babelArtifacts =
159                     babelClient.postArtifact(payload, artifactInfo.getArtifactName(), serviceVersion, distributionId);
160
161             // Sort Babel artifacts based on type
162             Map<ArtifactType, List<BabelArtifact>> artifactMap =
163                     babelArtifacts.stream().collect(Collectors.groupingBy(BabelArtifact::getType));
164
165             if (artifactMap.containsKey(BabelArtifact.ArtifactType.MODEL)) {
166                 modelArtifacts.addAll(
167                         getBabelArtifactConverter().convertToModel(artifactMap.get(BabelArtifact.ArtifactType.MODEL)));
168                 artifactMap.remove(BabelArtifact.ArtifactType.MODEL);
169             }
170
171             if (artifactMap.containsKey(BabelArtifact.ArtifactType.VNFCATALOG)) {
172                 catalogArtifacts.addAll(getBabelArtifactConverter()
173                         .convertToCatalog(artifactMap.get(BabelArtifact.ArtifactType.VNFCATALOG)));
174                 artifactMap.remove(BabelArtifact.ArtifactType.VNFCATALOG);
175             }
176
177             // Log unexpected artifact types
178             if (!artifactMap.isEmpty()) {
179                 logger.warn(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
180                         artifactInfo.getArtifactName() + " " + serviceVersion
181                                 + ". Unexpected artifact types returned by the babel service: "
182                                 + artifactMap.keySet().toString());
183             }
184
185         } catch (BabelArtifactParsingException e) {
186             logger.error(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
187                     "Error for artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + e);
188             throw new ProcessToscaArtifactsException(
189                     "An error occurred while trying to parse the Babel artifacts: " + e.getLocalizedMessage());
190         } catch (Exception e) {
191             logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
192                     "Error posting artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + " to Babel: "
193                             + e.getLocalizedMessage());
194             throw new ProcessToscaArtifactsException(
195                     "An error occurred while calling the Babel service: " + e.getLocalizedMessage());
196         }
197     }
198
199     BabelServiceClient createBabelServiceClient(IArtifactInfo artifact, String serviceVersion)
200             throws ProcessToscaArtifactsException {
201         BabelServiceClient babelClient;
202         try {
203             logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Creating Babel client");
204             babelClient = clientFactory.create(config);
205         } catch (Exception e) {
206             logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
207                     "Error posting artifact " + artifact.getArtifactName() + " " + serviceVersion + " to Babel: "
208                             + e.getLocalizedMessage());
209             throw new ProcessToscaArtifactsException(
210                     "An error occurred tyring to convert the tosca artifacts to xml artifacts: "
211                             + e.getLocalizedMessage());
212         }
213
214         return babelClient;
215     }
216
217     private void processModelQuerySpecArtifact(List<Artifact> modelArtifacts,
218             IDistributionClientDownloadResult downloadResult) throws BabelArtifactParsingException {
219         logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Processing named query artifact.");
220
221         IModelParser parser = new NamedQueryArtifactParser();
222
223         List<Artifact> parsedArtifacts =
224                 parser.parse(new String(downloadResult.getArtifactPayload()), downloadResult.getArtifactFilename());
225
226         if (parsedArtifactsExist(parsedArtifacts)) {
227             modelArtifacts.addAll(parsedArtifacts);
228         } else {
229             throw new BabelArtifactParsingException(
230                     "Could not parse generated XML: " + new String(downloadResult.getArtifactPayload()));
231         }
232     }
233
234     private boolean parsedArtifactsExist(List<Artifact> parsedArtifacts) {
235         return parsedArtifacts != null && !parsedArtifacts.isEmpty();
236     }
237
238     private NotificationPublisher getNotificationPublisher() {
239         if (notificationPublisher == null) {
240             notificationPublisher = new NotificationPublisher();
241         }
242
243         return notificationPublisher;
244     }
245
246     private BabelArtifactConverter getBabelArtifactConverter() {
247         if (babelArtifactConverter == null) {
248             babelArtifactConverter = new BabelArtifactConverter();
249         }
250
251         return babelArtifactConverter;
252     }
253 }