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