0601f4b2f1a73b644ae4dacb8c8c973a54530236
[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.sdc.api.IDistributionClient;
39 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
40 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
41 import org.onap.sdc.api.consumer.INotificationCallback;
42 import org.onap.sdc.api.notification.IArtifactInfo;
43 import org.onap.sdc.api.notification.INotificationData;
44 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
45 import org.onap.sdc.api.results.IDistributionClientResult;
46 import org.onap.sdc.impl.DistributionClientFactory;
47 import org.onap.sdc.utils.DistributionActionResultEnum;
48 import org.onap.sdc.utils.DistributionStatusEnum;
49
50 /**
51  * Handles reception of inputs from ONAP Service Design and Creation (SDC) from which policies may be decoded.
52  */
53 public class SdcReceptionHandler extends AbstractReceptionHandler implements INotificationCallback {
54
55     private static final Logger LOGGER = FlexLogger.getLogger(SdcReceptionHandler.class);
56
57     private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
58     private SdcReceptionHandlerConfigurationParameterGroup handlerParameters;
59     private IDistributionClient distributionClient;
60     private SdcConfiguration sdcConfig;
61     private volatile int nbOfNotificationsOngoing = 0;
62
63     private enum DistributionStatusType {
64         DOWNLOAD, DEPLOY
65     }
66
67     @Override
68     protected void initializeReception(final String parameterGroupName) throws PluginInitializationException {
69         handlerParameters = (SdcReceptionHandlerConfigurationParameterGroup) ParameterService.get(parameterGroupName);
70         initializeSdcClient();
71         startSdcClient();
72     }
73
74     @Override
75     public void destroy() throws PluginTerminationException {
76         LOGGER.debug("Going to stop the SDC Client...");
77         if (distributionClient != null) {
78             final IDistributionClientResult clientResult = distributionClient.stop();
79             if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
80                 final String message =
81                         "SDC client stop failed with reason:" + clientResult.getDistributionMessageResult();
82                 LOGGER.error(message);
83                 throw new PluginTerminationException(message);
84             }
85         }
86         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
87         LOGGER.debug("SDC Client is stopped successfully");
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                 if (nbOfNotificationsOngoing > 1) {
112                     --nbOfNotificationsOngoing;
113                 } else {
114                     nbOfNotificationsOngoing = 0;
115                     sdcReceptionHandlerStatus = newStatus;
116                 }
117                 break;
118             case BUSY:
119                 ++nbOfNotificationsOngoing;
120                 sdcReceptionHandlerStatus = newStatus;
121                 break;
122         }
123     }
124
125     /**
126      * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
127      *
128      * @return the {@link IDistributionClient} instance
129      */
130     protected IDistributionClient createSdcDistributionClient() {
131         return DistributionClientFactory.createDistributionClient();
132     }
133
134     /**
135      * Method to initialize the SDC client.
136      *
137      * @throws PluginInitializationException if the initialization of SDC Client fails
138      */
139     private void initializeSdcClient() throws PluginInitializationException {
140
141         LOGGER.debug("Initializing the SDC Client...");
142         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
143             final String message = "The SDC Client is already initialized";
144             LOGGER.error(message);
145             throw new PluginInitializationException(message);
146         }
147         sdcConfig = new SdcConfiguration(handlerParameters);
148         distributionClient = createSdcDistributionClient();
149         final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
150         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
151             changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
152             final String message =
153                     "SDC client initialization failed with reason:" + clientResult.getDistributionMessageResult();
154             LOGGER.error(message);
155             throw new PluginInitializationException(message);
156         }
157         LOGGER.debug("SDC Client is initialized successfully");
158         this.changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
159     }
160
161     /**
162      * Method to start the SDC client.
163      *
164      * @param configParameter the configuration parameters
165      * @throws PluginInitializationException if the start of SDC Client fails
166      */
167     private void startSdcClient() throws PluginInitializationException {
168
169         LOGGER.debug("Going to start the SDC Client...");
170         final IDistributionClientResult clientResult = distributionClient.start();
171         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
172             changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
173             final String message = "SDC client start failed with reason:" + clientResult.getDistributionMessageResult();
174             LOGGER.error(message);
175             throw new PluginInitializationException(message);
176         }
177         LOGGER.debug("SDC Client is started successfully");
178         this.changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
179     }
180
181     /**
182      * Method to process csar service artifacts from incoming SDC notification.
183      *
184      * @param notificationData the notification from SDC
185      */
186     public void processCsarServiceArtifacts(final INotificationData notificationData) {
187         boolean artifactsProcessedSuccessfully = true;
188
189         for (final IArtifactInfo artifact : notificationData.getServiceArtifacts()) {
190             try {
191                 final IDistributionClientDownloadResult resultArtifact =
192                         downloadTheArtifact(artifact, notificationData);
193                 final Path filePath = writeArtifactToFile(artifact, resultArtifact);
194                 final Csar csarObject = new Csar(filePath.toString());
195                 inputReceived(csarObject);
196                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
197                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_OK, null);
198                 deleteArtifactFile(filePath);
199             } catch (final ArtifactDownloadException | PolicyDecodingException exp) {
200                 LOGGER.error("Failed to process csar service artifacts ", exp);
201                 artifactsProcessedSuccessfully = false;
202                 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
203                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
204                         "Failed to deploy the artifact due to: " + exp.getMessage());
205             }
206         }
207         if (artifactsProcessedSuccessfully) {
208             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
209                     null);
210         } else {
211             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
212                     "Failed to process the artifact");
213         }
214     }
215
216     /**
217      * Method to download the distribution artifact.
218      *
219      * @param artifact the artifact
220      * @return the download result
221      * @throws ArtifactDownloadException if download fails
222      */
223     private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
224             final INotificationData notificationData) throws ArtifactDownloadException {
225
226         final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
227         if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
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         sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
236                 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
237         return downloadResult;
238     }
239
240     /**
241      * Method to write the downloaded distribution artifact to local file system.
242      *
243      * @param artifact the notification artifact
244      * @param resultArtifact the download result artifact
245      * @return the local path of written file
246      * @throws ArtifactDownloadException if error occurs while writing the artifact
247      */
248     private Path writeArtifactToFile(final IArtifactInfo artifact,
249             final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
250         try {
251             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
252             final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), null);
253             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
254                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
255                 return tempArtifactFile.toPath();
256             }
257         } catch (final Exception exp) {
258             final String message = "Failed to write artifact to local repository";
259             LOGGER.error(message, exp);
260             throw new ArtifactDownloadException(message, exp);
261         }
262     }
263
264     /**
265      * Method to delete the downloaded notification artifact from local file system.
266      *
267      * @param filePath the path of file
268      */
269     private void deleteArtifactFile(final Path filePath) {
270         try {
271             Files.deleteIfExists(filePath);
272         } catch (final IOException exp) {
273             LOGGER.error("Failed to delete the downloaded artifact file", exp);
274         }
275     }
276
277     /**
278      * Sends the distribution status to SDC using the input values.
279      *
280      * @param statusType the status type
281      * @param artifactUrl the artifact url
282      * @param distributionId the distribution id
283      * @param status the status
284      * @param errorReason the error reason
285      */
286     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
287             final String distributionId, final DistributionStatusEnum status, final String errorReason) {
288
289         IDistributionClientResult clientResult = null;
290         final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
291                 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
292                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
293         final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
294         switch (statusType) {
295             case DOWNLOAD:
296                 if (errorReason != null) {
297                     clientResult = distributionClient.sendDownloadStatus(message, errorReason);
298                 } else {
299                     clientResult = distributionClient.sendDownloadStatus(message);
300                 }
301                 break;
302             case DEPLOY:
303                 if (errorReason != null) {
304                     clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
305                 } else {
306                     clientResult = distributionClient.sendDeploymentStatus(message);
307                 }
308         }
309
310         final StringBuilder loggerMessage = new StringBuilder();
311         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
312                 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
313                 .append(statusType.name()).append(" Status: ").append(status.name());
314         if (errorReason != null) {
315             loggerMessage.append(" ErrorReason: ").append(errorReason);
316         }
317         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
318             loggerMessage.insert(0, "Failed sending ");
319             LOGGER.debug(loggerMessage);
320         } else {
321             loggerMessage.insert(0, "Successfully Sent ");
322             LOGGER.debug(loggerMessage);
323         }
324     }
325
326     /**
327      * Sends the component done status to SDC using the input values.
328      *
329      * @param distributionId the distribution Id
330      * @param status the distribution status
331      * @param errorReason the error reason
332      */
333     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
334             final String errorReason) {
335         IDistributionClientResult clientResult = null;
336         final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
337                 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
338                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
339         final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
340         if (errorReason == null) {
341             clientResult = distributionClient.sendComponentDoneStatus(message);
342         } else {
343             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
344         }
345
346         final StringBuilder loggerMessage = new StringBuilder();
347         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
348                 .append(distributionId).append(" Status: ").append(status.name());
349         if (errorReason != null) {
350             loggerMessage.append(" ErrorReason: ").append(errorReason);
351         }
352         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
353             loggerMessage.insert(0, "Failed sending ");
354             LOGGER.debug(loggerMessage);
355         } else {
356             loggerMessage.insert(0, "Successfully Sent ");
357             LOGGER.debug(loggerMessage);
358         }
359     }
360 }