443235b7d70997ea6d2281f0c7b26da8a0edd20d
[policy/distribution.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.distribution.reception.handling.sdc;
22
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28
29 import org.onap.policy.common.logging.flexlogger.FlexLogger;
30 import org.onap.policy.common.logging.flexlogger.Logger;
31 import org.onap.policy.common.parameters.ParameterService;
32 import org.onap.policy.distribution.model.Csar;
33 import org.onap.policy.distribution.reception.decoding.PluginInitializationException;
34 import org.onap.policy.distribution.reception.decoding.PluginTerminationException;
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.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.utils.DistributionActionResultEnum;
49 import org.onap.sdc.utils.DistributionStatusEnum;
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 = FlexLogger.getLogger(SdcReceptionHandler.class);
57
58     private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
59     private SdcReceptionHandlerConfigurationParameterGroup handlerParameters;
60     private IDistributionClient distributionClient;
61     private SdcConfiguration sdcConfig;
62     private volatile int nbOfNotificationsOngoing = 0;
63
64     private enum DistributionStatusType {
65         DOWNLOAD, DEPLOY
66     }
67
68     @Override
69     protected void initializeReception(final String parameterGroupName) throws PluginInitializationException {
70         handlerParameters = ParameterService.get(parameterGroupName);
71         initializeSdcClient();
72         startSdcClient();
73     }
74
75     @Override
76     public void destroy() throws PluginTerminationException {
77         LOGGER.debug("Going to stop the SDC Client...");
78         if (distributionClient != null) {
79             final IDistributionClientResult clientResult = distributionClient.stop();
80             if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
81                 final String message =
82                         "SDC client stop failed with reason:" + clientResult.getDistributionMessageResult();
83                 LOGGER.error(message);
84                 throw new PluginTerminationException(message);
85             }
86         }
87         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
88         LOGGER.debug("SDC Client is stopped successfully");
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 final 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;
116                 sdcReceptionHandlerStatus = newStatus;
117                 break;
118         }
119     }
120
121     /**
122      * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
123      *
124      * @return the {@link IDistributionClient} instance
125      */
126     protected IDistributionClient createSdcDistributionClient() {
127         return DistributionClientFactory.createDistributionClient();
128     }
129
130     /**
131      * Method to initialize the SDC client.
132      *
133      * @throws PluginInitializationException if the initialization of SDC Client fails
134      */
135     private void initializeSdcClient() throws PluginInitializationException {
136
137         LOGGER.debug("Initializing the SDC Client...");
138         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
139             final String message = "The SDC Client is already initialized";
140             LOGGER.error(message);
141             throw new PluginInitializationException(message);
142         }
143         sdcConfig = new SdcConfiguration(handlerParameters);
144         distributionClient = createSdcDistributionClient();
145         final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
146         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
147             changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
148             final String message =
149                     "SDC client initialization failed with reason:" + clientResult.getDistributionMessageResult();
150             LOGGER.error(message);
151             throw new PluginInitializationException(message);
152         }
153         LOGGER.debug("SDC Client is initialized successfully");
154         this.changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
155     }
156
157     /**
158      * Method to start the SDC client.
159      *
160      * @param configParameter the configuration parameters
161      * @throws PluginInitializationException if the start of SDC Client fails
162      */
163     private void startSdcClient() throws PluginInitializationException {
164
165         LOGGER.debug("Going to start the SDC Client...");
166         final IDistributionClientResult clientResult = distributionClient.start();
167         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
168             changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
169             final String message = "SDC client start failed with reason:" + clientResult.getDistributionMessageResult();
170             LOGGER.error(message);
171             throw new PluginInitializationException(message);
172         }
173         LOGGER.debug("SDC Client is started successfully");
174         this.changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
175     }
176
177     /**
178      * Method to process csar service artifacts from incoming SDC notification.
179      *
180      * @param notificationData the notification from SDC
181      */
182     public void processCsarServiceArtifacts(final INotificationData notificationData) {
183         boolean artifactsProcessedSuccessfully = true;
184         DistributionStatisticsManager.updateTotalDistributionCount();
185         for (final IArtifactInfo artifact : notificationData.getServiceArtifacts()) {
186             try {
187                 final IDistributionClientDownloadResult resultArtifact =
188                         downloadTheArtifact(artifact, notificationData);
189                 final Path filePath = writeArtifactToFile(artifact, resultArtifact);
190                 final Csar csarObject = new Csar(filePath.toString());
191                 inputReceived(csarObject);
192                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
193                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_OK, null);
194                 deleteArtifactFile(filePath);
195             } catch (final ArtifactDownloadException | PolicyDecodingException exp) {
196                 LOGGER.error("Failed to process csar service artifacts ", exp);
197                 artifactsProcessedSuccessfully = false;
198                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
199                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
200                         "Failed to deploy the artifact due to: " + exp.getMessage());
201             }
202         }
203         if (artifactsProcessedSuccessfully) {
204             DistributionStatisticsManager.updateDistributionSuccessCount();
205             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
206                     null);
207         } else {
208             DistributionStatisticsManager.updateDistributionFailureCount();
209             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
210                     "Failed to process the artifact");
211         }
212     }
213
214     /**
215      * Method to download the distribution artifact.
216      *
217      * @param artifact the artifact
218      * @return the download result
219      * @throws ArtifactDownloadException if download fails
220      */
221     private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
222             final INotificationData notificationData) throws ArtifactDownloadException {
223
224         DistributionStatisticsManager.updateTotalDownloadCount();
225         final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
226         if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
227             DistributionStatisticsManager.updateDownloadFailureCount();
228             final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
229                     + downloadResult.getDistributionMessageResult();
230             LOGGER.error(message);
231             sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
232                     notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
233             throw new ArtifactDownloadException(message);
234         }
235         DistributionStatisticsManager.updateDownloadSuccessCount();
236         sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
237                 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
238         return downloadResult;
239     }
240
241     /**
242      * Method to write the downloaded distribution artifact to local file system.
243      *
244      * @param artifact the notification artifact
245      * @param resultArtifact the download result artifact
246      * @return the local path of written file
247      * @throws ArtifactDownloadException if error occurs while writing the artifact
248      */
249     private Path writeArtifactToFile(final IArtifactInfo artifact,
250             final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
251         try {
252             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
253             final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), null);
254             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
255                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
256                 return tempArtifactFile.toPath();
257             }
258         } catch (final Exception exp) {
259             final String message = "Failed to write artifact to local repository";
260             LOGGER.error(message, exp);
261             throw new ArtifactDownloadException(message, exp);
262         }
263     }
264
265     /**
266      * Method to delete the downloaded notification artifact from local file system.
267      *
268      * @param filePath the path of file
269      */
270     private void deleteArtifactFile(final Path filePath) {
271         try {
272             Files.deleteIfExists(filePath);
273         } catch (final IOException exp) {
274             LOGGER.error("Failed to delete the downloaded artifact file", exp);
275         }
276     }
277
278     /**
279      * Sends the distribution status to SDC using the input values.
280      *
281      * @param statusType the status type
282      * @param artifactUrl the artifact url
283      * @param distributionId the distribution id
284      * @param status the status
285      * @param errorReason the error reason
286      */
287     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
288             final String distributionId, final DistributionStatusEnum status, final String errorReason) {
289
290         IDistributionClientResult clientResult;
291         final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
292                 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
293                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
294         final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
295         if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
296             if (errorReason != null) {
297                 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
298             } else {
299                 clientResult = distributionClient.sendDownloadStatus(message);
300             }
301         } else {
302             if (errorReason != null) {
303                 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
304             } else {
305                 clientResult = distributionClient.sendDeploymentStatus(message);
306             }
307         }
308         final StringBuilder loggerMessage = new StringBuilder();
309         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
310                 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
311                 .append(statusType.name()).append(" Status: ").append(status.name());
312         if (errorReason != null) {
313             loggerMessage.append(" ErrorReason: ").append(errorReason);
314         }
315         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
316             loggerMessage.insert(0, "Failed sending ");
317             LOGGER.debug(loggerMessage);
318         } else {
319             loggerMessage.insert(0, "Successfully Sent ");
320             LOGGER.debug(loggerMessage);
321         }
322     }
323
324     /**
325      * Sends the component done status to SDC using the input values.
326      *
327      * @param distributionId the distribution Id
328      * @param status the distribution status
329      * @param errorReason the error reason
330      */
331     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
332             final String errorReason) {
333         IDistributionClientResult clientResult;
334         final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
335                 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
336                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
337         final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
338         if (errorReason == null) {
339             clientResult = distributionClient.sendComponentDoneStatus(message);
340         } else {
341             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
342         }
343
344         final StringBuilder loggerMessage = new StringBuilder();
345         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
346                 .append(distributionId).append(" Status: ").append(status.name());
347         if (errorReason != null) {
348             loggerMessage.append(" ErrorReason: ").append(errorReason);
349         }
350         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
351             loggerMessage.insert(0, "Failed sending ");
352             LOGGER.debug(loggerMessage);
353         } else {
354             loggerMessage.insert(0, "Successfully Sent ");
355             LOGGER.debug(loggerMessage);
356         }
357     }
358
359     /**
360      * Handle the status change of {@link SdcReceptionHandler} to Idle.
361      *
362      * @param newStatus the new status
363      */
364     private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
365         if (nbOfNotificationsOngoing > 1) {
366             --nbOfNotificationsOngoing;
367         } else {
368             nbOfNotificationsOngoing = 0;
369             sdcReceptionHandlerStatus = newStatus;
370         }
371     }
372 }