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