ff915613eb6472ed80a6e76c2942ec3ed4a33a3d
[policy/distribution.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  *  Copyright (C) 2019, 2022 Nordix Foundation.
5  *  Modifications Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6  *  Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
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  *
20  * SPDX-License-Identifier: Apache-2.0
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.distribution.reception.handling.sdc;
25
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.util.Optional;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import org.onap.policy.common.parameters.ParameterService;
34 import org.onap.policy.distribution.model.Csar;
35 import org.onap.policy.distribution.reception.decoding.PolicyDecodingException;
36 import org.onap.policy.distribution.reception.handling.AbstractReceptionHandler;
37 import org.onap.policy.distribution.reception.handling.sdc.SdcClientHandler.SdcClientOperationType;
38 import org.onap.policy.distribution.reception.handling.sdc.exceptions.ArtifactDownloadException;
39 import org.onap.policy.distribution.reception.statistics.DistributionStatisticsManager;
40 import org.onap.sdc.api.IDistributionClient;
41 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
42 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
43 import org.onap.sdc.api.consumer.INotificationCallback;
44 import org.onap.sdc.api.notification.IArtifactInfo;
45 import org.onap.sdc.api.notification.INotificationData;
46 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
47 import org.onap.sdc.api.results.IDistributionClientResult;
48 import org.onap.sdc.impl.DistributionClientFactory;
49 import org.onap.sdc.impl.DistributionClientImpl;
50 import org.onap.sdc.utils.DistributionActionResultEnum;
51 import org.onap.sdc.utils.DistributionStatusEnum;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * Handles reception of inputs from ONAP Service Design and Creation (SDC) from which policies may be decoded.
57  */
58 public class SdcReceptionHandler extends AbstractReceptionHandler implements INotificationCallback {
59
60     private static final Logger LOGGER = LoggerFactory.getLogger(SdcReceptionHandler.class);
61     private static final String SECONDS = "Seconds";
62
63     private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
64     private IDistributionClient distributionClient;
65     private SdcConfiguration sdcConfig;
66     private final AtomicInteger nbOfNotificationsOngoing = new AtomicInteger();
67     private int retryDelay;
68     private SdcClientHandler sdcClientHandler;
69
70     private enum DistributionStatusType {
71         DOWNLOAD, DEPLOY
72     }
73
74     @Override
75     protected void initializeReception(final String parameterGroupName) {
76         final SdcReceptionHandlerConfigurationParameterGroup handlerParameters =
77             ParameterService.get(parameterGroupName);
78         retryDelay = handlerParameters.getRetryDelay() < 30 ? 30 : handlerParameters.getRetryDelay();
79         sdcConfig = new SdcConfiguration(handlerParameters);
80         distributionClient = createSdcDistributionClient();
81         sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.START, retryDelay);
82     }
83
84     @Override
85     public void destroy() {
86         if (distributionClient != null) {
87             sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.STOP, retryDelay);
88         }
89     }
90
91     @Override
92     public void activateCallback(final INotificationData notificationData) {
93         LOGGER.debug("Receieved the notification from SDC with ID: {}", notificationData.getDistributionID());
94         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.BUSY);
95         processCsarServiceArtifacts(notificationData);
96         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
97         LOGGER.debug("Processed the notification from SDC with ID: {}", notificationData.getDistributionID());
98     }
99
100     /**
101      * Method to change the status of this reception handler instance.
102      *
103      * @param newStatus the new status
104      */
105     private synchronized void changeSdcReceptionHandlerStatus(final SdcReceptionHandlerStatus newStatus) {
106         switch (newStatus) {
107             case INIT:
108             case STOPPED:
109                 sdcReceptionHandlerStatus = newStatus;
110                 break;
111             case IDLE:
112                 handleIdleStatusChange(newStatus);
113                 break;
114             case BUSY:
115                 nbOfNotificationsOngoing.incrementAndGet();
116                 sdcReceptionHandlerStatus = newStatus;
117                 break;
118             default:
119                 break;
120         }
121     }
122
123     /**
124      * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
125      *
126      * @return the {@link IDistributionClient} instance
127      */
128     protected IDistributionClient createSdcDistributionClient() {
129         return new DistributionClientImpl();
130     }
131
132     /**
133      * Method to initialize the SDC client.
134      */
135     protected void initializeSdcClient() {
136
137         LOGGER.debug("Initializing the SDC Client...");
138         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
139             LOGGER.error("The SDC Client is already initialized");
140             return;
141         }
142         final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
143         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
144             LOGGER.error("SDC client initialization failed with reason: {}. Initialization will be retried after {} {}",
145                 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
146             return;
147         }
148         LOGGER.debug("SDC Client is initialized successfully");
149         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
150     }
151
152     /**
153      * Method to start the SDC client.
154      */
155     protected void startSdcClient() {
156
157         LOGGER.debug("Going to start the SDC Client...");
158         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.INIT) {
159             LOGGER.error("The SDC Client is not initialized");
160             return;
161         }
162         final IDistributionClientResult clientResult = distributionClient.start();
163         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
164             LOGGER.error("SDC client start failed with reason: {}. Start will be retried after {} {}",
165                 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
166             return;
167         }
168         LOGGER.debug("SDC Client is started successfully");
169         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
170         sdcClientHandler.cancel();
171     }
172
173     /**
174      * Method to stop the SDC client.
175      */
176     protected void stopSdcClient() {
177         LOGGER.debug("Going to stop the SDC Client...");
178         final IDistributionClientResult clientResult = distributionClient.stop();
179         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
180             LOGGER.error("SDC client stop failed with reason: {}. Stop will be retried after {} {}",
181                 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
182             return;
183         }
184         LOGGER.debug("SDC Client is stopped successfully");
185         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
186         sdcClientHandler.cancel();
187     }
188
189     /**
190      * Method to process csar service artifacts from incoming SDC notification.
191      *
192      * @param notificationData the notification from SDC
193      */
194     public void processCsarServiceArtifacts(final INotificationData notificationData) {
195         var artifactsProcessedSuccessfully = true;
196         DistributionStatisticsManager.updateTotalDistributionCount();
197         for (final IArtifactInfo artifact : notificationData.getServiceArtifacts()) {
198             try {
199                 final IDistributionClientDownloadResult resultArtifact =
200                     downloadTheArtifact(artifact, notificationData);
201                 final var filePath = writeArtifactToFile(artifact, resultArtifact);
202                 final var csarObject = new Csar(filePath.toString());
203                 inputReceived(csarObject);
204                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
205                     notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_OK, null);
206                 deleteArtifactFile(filePath);
207             } catch (final ArtifactDownloadException | PolicyDecodingException exp) {
208                 LOGGER.error("Failed to process csar service artifacts ", exp);
209                 artifactsProcessedSuccessfully = false;
210                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
211                     notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
212                     "Failed to deploy the artifact due to: " + exp.getMessage());
213             }
214         }
215         if (artifactsProcessedSuccessfully) {
216             DistributionStatisticsManager.updateDistributionSuccessCount();
217             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
218                 null);
219         } else {
220             DistributionStatisticsManager.updateDistributionFailureCount();
221             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
222                 "Failed to process the artifact");
223         }
224     }
225
226     /**
227      * Method to download the distribution artifact.
228      *
229      * @param artifact the artifact
230      * @return the download result
231      * @throws ArtifactDownloadException if download fails
232      */
233     private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
234                                                                   final INotificationData notificationData)
235         throws ArtifactDownloadException {
236
237         DistributionStatisticsManager.updateTotalDownloadCount();
238         final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
239         if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
240             DistributionStatisticsManager.updateDownloadFailureCount();
241             final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
242                 + downloadResult.getDistributionMessageResult();
243             LOGGER.error(message);
244             sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
245                 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
246             throw new ArtifactDownloadException(message);
247         }
248         DistributionStatisticsManager.updateDownloadSuccessCount();
249         sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
250             notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
251         return downloadResult;
252     }
253
254     /**
255      * Method to write the downloaded distribution artifact to local file system.
256      *
257      * @param artifact       the notification artifact
258      * @param resultArtifact the download result artifact
259      * @return the local path of written file
260      * @throws ArtifactDownloadException if error occurs while writing the artifact
261      */
262     private Path writeArtifactToFile(final IArtifactInfo artifact,
263                                      final IDistributionClientDownloadResult resultArtifact)
264         throws ArtifactDownloadException {
265         try {
266             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
267
268             final var tempArtifactFile = Optional.ofNullable(safelyCreateFile(artifact.getArtifactName()))
269                 .orElseThrow(() -> new ArtifactDownloadException("Failed to create temporary file."));
270             try (var fileOutputStream = new FileOutputStream(tempArtifactFile)) {
271                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
272                 return tempArtifactFile.toPath();
273             }
274         } catch (final Exception exp) {
275             throw new ArtifactDownloadException("Failed to write artifact to local repository", exp);
276         }
277     }
278
279     /**
280      * Method to delete the downloaded notification artifact from local file system.
281      *
282      * @param filePath the path of file
283      */
284     private void deleteArtifactFile(final Path filePath) {
285         try {
286             Files.deleteIfExists(filePath);
287         } catch (final IOException exp) {
288             LOGGER.error("Failed to delete the downloaded artifact file", exp);
289         }
290     }
291
292     /**
293      * Sends the distribution status to SDC using the input values.
294      *
295      * @param statusType     the status type
296      * @param artifactUrl    the artifact url
297      * @param distributionId the distribution id
298      * @param status         the status
299      * @param errorReason    the error reason
300      */
301     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
302                                         final String distributionId, final DistributionStatusEnum status,
303                                         final String errorReason) {
304
305         IDistributionClientResult clientResult;
306         final IDistributionStatusMessage message = DistributionStatusMessage.builder().artifactUrl(artifactUrl)
307             .consumerId(sdcConfig.getConsumerID()).distributionId(distributionId).distributionStatus(status)
308             .timestamp(System.currentTimeMillis()).build();
309         if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
310             if (errorReason != null) {
311                 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
312             } else {
313                 clientResult = distributionClient.sendDownloadStatus(message);
314             }
315         } else {
316             if (errorReason != null) {
317                 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
318             } else {
319                 clientResult = distributionClient.sendDeploymentStatus(message);
320             }
321         }
322         final var loggerMessage = new StringBuilder();
323         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
324             .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
325             .append(statusType.name()).append(" Status: ").append(status.name());
326         if (errorReason != null) {
327             loggerMessage.append(" ErrorReason: ").append(errorReason);
328         }
329         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
330             loggerMessage.insert(0, "Failed sending ");
331             LOGGER.debug("Failed sending {}", loggerMessage);
332         } else {
333             loggerMessage.insert(0, "Successfully Sent ");
334             LOGGER.debug("Successfully Sent {}", loggerMessage);
335         }
336     }
337
338     /**
339      * Sends the component done status to SDC using the input values.
340      *
341      * @param distributionId the distribution Id
342      * @param status         the distribution status
343      * @param errorReason    the error reason
344      */
345     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
346                                          final String errorReason) {
347         IDistributionClientResult clientResult;
348         final IComponentDoneStatusMessage message = ComponentDoneStatusMessage.builder()
349             .consumerId(sdcConfig.getConsumerID()).distributionId(distributionId).distributionStatus(status)
350             .timestamp(System.currentTimeMillis()).build();
351         if (errorReason == null) {
352             clientResult = distributionClient.sendComponentDoneStatus(message);
353         } else {
354             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
355         }
356
357         final var loggerMessage = new StringBuilder();
358         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
359             .append(distributionId).append(" Status: ").append(status.name());
360         if (errorReason != null) {
361             loggerMessage.append(" ErrorReason: ").append(errorReason);
362         }
363         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
364             LOGGER.debug("Failed sending {}", loggerMessage);
365         } else {
366             LOGGER.debug("Successfully Sent {}", loggerMessage);
367         }
368     }
369
370     /**
371      * Handle the status change of {@link SdcReceptionHandler} to Idle.
372      *
373      * @param newStatus the new status
374      */
375     private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
376         if (nbOfNotificationsOngoing.getAndUpdate(curval -> Math.max(0, curval - 1)) == 0) {
377             sdcReceptionHandlerStatus = newStatus;
378         }
379     }
380
381     private File safelyCreateFile(String prefix) throws IOException {
382         File file = Files.createTempFile(prefix, ".csar").toFile();
383         if (file.setReadable(true, false)
384             && file.setWritable(true, true)) {
385             return file;
386         }
387         return null;
388     }
389 }