Send sdc notifs
[clamp.git] / src / main / java / org / onap / clamp / clds / sdc / controller / SdcSingleController.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights
6  *                             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  * ============LICENSE_END============================================
20  * Modifications copyright (c) 2018 Nokia
21  * ===================================================================
22  *
23  */
24
25 package org.onap.clamp.clds.sdc.controller;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29
30 import java.util.Date;
31 import java.util.Map.Entry;
32 import java.util.concurrent.ThreadLocalRandom;
33
34 import org.onap.clamp.clds.config.ClampProperties;
35 import org.onap.clamp.clds.config.sdc.SdcSingleControllerConfiguration;
36 import org.onap.clamp.clds.exception.sdc.controller.BlueprintParserException;
37 import org.onap.clamp.clds.exception.sdc.controller.CsarHandlerException;
38 import org.onap.clamp.clds.exception.sdc.controller.SdcArtifactInstallerException;
39 import org.onap.clamp.clds.exception.sdc.controller.SdcControllerException;
40 import org.onap.clamp.clds.exception.sdc.controller.SdcDownloadException;
41 import org.onap.clamp.clds.sdc.controller.installer.BlueprintArtifact;
42 import org.onap.clamp.clds.sdc.controller.installer.CsarHandler;
43 import org.onap.clamp.clds.util.LoggingUtils;
44 import org.onap.clamp.loop.CsarInstaller;
45 import org.onap.sdc.api.IDistributionClient;
46 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
47 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
48 import org.onap.sdc.api.consumer.INotificationCallback;
49 import org.onap.sdc.api.notification.IArtifactInfo;
50 import org.onap.sdc.api.notification.INotificationData;
51 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
52 import org.onap.sdc.api.results.IDistributionClientResult;
53 import org.onap.sdc.impl.DistributionClientFactory;
54 import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException;
55 import org.onap.sdc.utils.DistributionActionResultEnum;
56 import org.onap.sdc.utils.DistributionStatusEnum;
57
58 /**
59  * This class handles one sdc controller defined in the config.
60  */
61 public class SdcSingleController {
62
63     private static final EELFLogger logger = EELFManager.getInstance().getLogger(SdcSingleController.class);
64     private boolean isSdcClientAutoManaged = false;
65     private CsarInstaller csarInstaller;
66     private ClampProperties refProp;
67     /**
68      * The constant CONFIG_SDC_FOLDER.
69      */
70     public static final String CONFIG_SDC_FOLDER = "sdc.csarFolder";
71     private int nbOfNotificationsOngoing = 0;
72     private SdcSingleControllerStatus controllerStatus = SdcSingleControllerStatus.STOPPED;
73     private SdcSingleControllerConfiguration sdcConfig;
74     private IDistributionClient distributionClient;
75
76     /**
77      * Inner class for Notification callback.
78      */
79     private final class SdcNotificationCallBack implements INotificationCallback {
80
81         private SdcSingleController sdcController;
82
83         /**
84          * Instantiates a new Sdc notification call back.
85          *
86          * @param controller the controller
87          */
88         SdcNotificationCallBack(SdcSingleController controller) {
89             sdcController = controller;
90         }
91
92         /**
93          * This method can be called multiple times at the same moment. The controller
94          * must be thread safe !
95          */
96         @Override
97         public void activateCallback(INotificationData notificationData) {
98             Date startTime = new Date();
99             logger.info("Receive a callback notification in SDC, nb of resources: "
100                     + notificationData.getResources().size());
101             sdcController.treatNotification(notificationData);
102             LoggingUtils.setTimeContext(startTime, new Date());
103             LoggingUtils.setResponseContext("0", "SDC Notification received and processed successfully",
104                     this.getClass().getName());
105         }
106     }
107
108     /**
109      * Gets nb of notifications ongoing.
110      *
111      * @return the nb of notifications ongoing
112      */
113     public int getNbOfNotificationsOngoing() {
114         return nbOfNotificationsOngoing;
115     }
116
117     private void changeControllerStatusIdle() {
118         if (this.nbOfNotificationsOngoing > 1) {
119             --this.nbOfNotificationsOngoing;
120         } else {
121             this.nbOfNotificationsOngoing = 0;
122             this.controllerStatus = SdcSingleControllerStatus.IDLE;
123         }
124     }
125
126     /**
127      * Change controller status.
128      *
129      * @param newControllerStatus the new controller status
130      */
131     protected final synchronized void changeControllerStatus(SdcSingleControllerStatus newControllerStatus) {
132         switch (newControllerStatus) {
133             case BUSY:
134                 ++this.nbOfNotificationsOngoing;
135                 this.controllerStatus = newControllerStatus;
136                 break;
137             case IDLE:
138                 this.changeControllerStatusIdle();
139                 break;
140             default:
141                 this.controllerStatus = newControllerStatus;
142                 break;
143         }
144     }
145
146     /**
147      * Gets controller status.
148      *
149      * @return the controller status
150      */
151     public final synchronized SdcSingleControllerStatus getControllerStatus() {
152         return this.controllerStatus;
153     }
154
155     /**
156      * Instantiates a new Sdc single controller.
157      *
158      * @param clampProp          the clamp prop
159      * @param csarInstaller      the csar installer
160      * @param sdcSingleConfig    the sdc single config
161      * @param distributionClient the distribution client
162      */
163     public SdcSingleController(ClampProperties clampProp, CsarInstaller csarInstaller,
164                                SdcSingleControllerConfiguration sdcSingleConfig,
165                                IDistributionClient distributionClient) {
166         this.distributionClient = distributionClient;
167         isSdcClientAutoManaged = (distributionClient == null);
168         this.sdcConfig = sdcSingleConfig;
169         this.refProp = clampProp;
170         this.csarInstaller = csarInstaller;
171     }
172
173     /**
174      * This method initializes the SDC Controller and the SDC Client.
175      *
176      * @throws SdcControllerException It throws an exception if the SDC Client
177      *                                cannot be instantiated or if an init attempt
178      *                                is done when already initialized
179      */
180     public void initSdc() throws SdcControllerException {
181         logger.info("Attempt to initialize the SDC Controller: " + sdcConfig.getSdcControllerName());
182         if (this.getControllerStatus() != SdcSingleControllerStatus.STOPPED) {
183             throw new SdcControllerException("The controller is already initialized, call the closeSDC method first");
184         }
185         if (distributionClient == null) {
186             distributionClient = DistributionClientFactory.createDistributionClient();
187         }
188         IDistributionClientResult result = distributionClient.init(sdcConfig, new SdcNotificationCallBack(this));
189         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
190             logger.error("SDC distribution client init failed with reason:" + result.getDistributionMessageResult());
191             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
192             throw new SdcControllerException("Initialization of the SDC Controller failed with reason: "
193                     + result.getDistributionMessageResult());
194         }
195         logger.info("SDC Controller successfully initialized: " + sdcConfig.getSdcControllerName());
196         logger.info("Attempt to start the SDC Controller: " + sdcConfig.getSdcControllerName());
197         result = this.distributionClient.start();
198         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
199             logger.error("SDC distribution client start failed with reason:" + result.getDistributionMessageResult());
200             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
201             throw new SdcControllerException(
202                     "Startup of the SDC Controller failed with reason: " + result.getDistributionMessageResult());
203         }
204         logger.info("SDC Controller successfully started: " + sdcConfig.getSdcControllerName());
205         this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
206     }
207
208     /**
209      * This method closes the SDC Controller and the SDC Client.
210      *
211      * @throws SdcControllerException It throws an exception if the SDC Client
212      *                                cannot be closed because it's currently BUSY
213      *                                in processing notifications.
214      */
215     public void closeSdc() throws SdcControllerException {
216         if (this.getControllerStatus() == SdcSingleControllerStatus.BUSY) {
217             throw new SdcControllerException("Cannot close the SDC controller as it's currently in BUSY state");
218         }
219         if (this.distributionClient != null) {
220             this.distributionClient.stop();
221             // If auto managed we can set it to Null, SdcController controls it.
222             // In the other case the client of this class has specified it, so
223             // we can't reset it
224             if (isSdcClientAutoManaged) {
225                 // Next init will initialize it with a new SDC Client
226                 this.distributionClient = null;
227             }
228         }
229         this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
230     }
231
232     private void sendAllNotificationForCsarHandler(INotificationData notificationData, CsarHandler csar,
233                                                    NotificationType notificationType,
234                                                    DistributionStatusEnum distributionStatus, String errorMessage) {
235         if (csar != null) {
236             // Notify for the CSAR
237             this.sendSdcNotification(notificationType, csar.getArtifactElement().getArtifactURL(),
238                     sdcConfig.getConsumerID(), notificationData.getDistributionID(), distributionStatus, errorMessage,
239                     System.currentTimeMillis());
240             // Notify for all VF resources found
241             for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) {
242                 // Normally always 1 artifact in resource for Clamp as we
243                 // specified
244                 // only VF_METADATA type
245                 this.sendSdcNotification(notificationType,
246                         blueprint.getValue().getResourceAttached().getArtifacts().get(0).getArtifactURL(),
247                         sdcConfig.getConsumerID(), notificationData.getDistributionID(), distributionStatus,
248                         errorMessage, System.currentTimeMillis());
249             }
250         } else {
251             this.sendSdcNotification(notificationType, null, sdcConfig.getConsumerID(),
252                     notificationData.getDistributionID(), distributionStatus, errorMessage, System.currentTimeMillis());
253         }
254     }
255
256     /**
257      * This method processes the notification received from Sdc.
258      *
259      * @param notificationData The INotificationData
260      */
261     public void treatNotification(INotificationData notificationData) {
262         CsarHandler csar = null;
263         try {
264             // wait for a random time, so that 2 running Clamp will not treat
265             // the same Notification at the same time
266             Thread.sleep(ThreadLocalRandom.current().nextInt(1, 10) * 1000L);
267             logger.info("Notification received for service UUID:" + notificationData.getServiceUUID());
268             this.changeControllerStatus(SdcSingleControllerStatus.BUSY);
269             csar = new CsarHandler(notificationData, this.sdcConfig.getSdcControllerName(),
270                     refProp.getStringValue(CONFIG_SDC_FOLDER));
271             csar.save(downloadTheArtifact(csar.getArtifactElement()));
272             if (csarInstaller.isCsarAlreadyDeployed(csar)) {
273                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
274                         DistributionStatusEnum.ALREADY_DOWNLOADED, null);
275                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
276                         DistributionStatusEnum.ALREADY_DEPLOYED, null);
277             } else {
278                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
279                         DistributionStatusEnum.DOWNLOAD_OK, null);
280                 csarInstaller.installTheCsar(csar);
281                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
282                         DistributionStatusEnum.DEPLOY_OK, null);
283                 this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_OK, null);
284             }
285         } catch (SdcArtifactInstallerException | SdcToscaParserException e) {
286             logger.error("SdcArtifactInstallerException exception caught during the notification processing", e);
287             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
288                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
289             this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_ERROR, e.getMessage());
290         } catch (SdcDownloadException | CsarHandlerException e) {
291             logger.error("SdcDownloadException exception caught during the notification processing", e);
292             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
293                     DistributionStatusEnum.DOWNLOAD_ERROR, e.getMessage());
294             this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_ERROR, e.getMessage());
295         } catch (InterruptedException e) {
296             logger.error("Interrupt exception caught during the notification processing", e);
297             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
298                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
299             this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_ERROR, e.getMessage());
300             Thread.currentThread().interrupt();
301         } catch (BlueprintParserException e) {
302             logger.error("BlueprintParser exception caught during the notification processing", e);
303             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
304                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
305             this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_ERROR, e.getMessage());
306         } catch (RuntimeException e) {
307             logger.error("Unexpected exception caught during the notification processing", e);
308             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
309                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
310             this.sendComponentStatus(notificationData, DistributionStatusEnum.COMPONENT_DONE_ERROR, e.getMessage());
311         } finally {
312             this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
313         }
314     }
315
316     private enum NotificationType {
317         /**
318          * Download notification type.
319          */
320         DOWNLOAD,
321         /**
322          * Deploy notification type.
323          */
324         DEPLOY
325     }
326
327     private IDistributionClientDownloadResult downloadTheArtifact(IArtifactInfo artifact) throws SdcDownloadException {
328         logger.info("Trying to download the artifact : " + artifact.getArtifactURL() + " UUID: "
329                 + artifact.getArtifactUUID());
330         IDistributionClientDownloadResult downloadResult;
331         try {
332             downloadResult = distributionClient.download(artifact);
333             if (null == downloadResult) {
334                 logger.info("downloadResult is Null for: " + artifact.getArtifactUUID());
335                 return null;
336             }
337         } catch (RuntimeException e) {
338             throw new SdcDownloadException("Exception caught when downloading the artifact", e);
339         }
340         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
341             logger.info("Successfully downloaded the artifact " + artifact.getArtifactURL() + " UUID "
342                     + artifact.getArtifactUUID() + "Size of payload " + downloadResult.getArtifactPayload().length);
343         } else {
344             throw new SdcDownloadException("Artifact " + artifact.getArtifactName()
345                     + " could not be downloaded from SDC URL " + artifact.getArtifactURL() + " UUID "
346                     + artifact.getArtifactUUID() + ")" + System.lineSeparator() + "Error message is "
347                     + downloadResult.getDistributionMessageResult() + System.lineSeparator());
348         }
349         return downloadResult;
350     }
351
352     private void sendSdcNotification(NotificationType notificationType, String artifactUrl, String consumerId,
353                                      String distributionId, DistributionStatusEnum status, String errorReason,
354                                      long timestamp) {
355         String event = "Sending " + notificationType.name() + "(" + status.name() + ")"
356                 + " notification to SDC for artifact:" + artifactUrl;
357         if (errorReason != null) {
358             event = event + "(" + errorReason + ")";
359         }
360         logger.info(event);
361         String action = "";
362         try {
363             IDistributionStatusMessage message = new DistributionStatusMessage(artifactUrl, consumerId, distributionId,
364                     status, timestamp);
365             switch (notificationType) {
366                 case DOWNLOAD:
367                     this.sendDownloadStatus(message, errorReason);
368                     action = "sendDownloadStatus";
369                     break;
370                 case DEPLOY:
371                     this.sendDeploymentStatus(message, errorReason);
372                     action = "sendDeploymentdStatus";
373                     break;
374                 default:
375                     break;
376             }
377         } catch (RuntimeException e) {
378             logger.warn("Unable to send the SDC Notification (" + action + ") due to an exception", e);
379         }
380         logger.info("SDC Notification sent successfully(" + action + ")");
381     }
382
383     private void sendComponentStatus(INotificationData notificationData, DistributionStatusEnum status,
384                                      String errorReason) {
385         try {
386             IComponentDoneStatusMessage message = new IComponentDoneStatusMessage() {
387
388                 @Override public String getDistributionID() {
389                     return notificationData.getDistributionID();
390                 }
391
392                 @Override public String getConsumerID() {
393                     return sdcConfig.getConsumerID();
394                 }
395
396                 @Override public long getTimestamp() {
397                     return System.currentTimeMillis();
398                 }
399
400                 @Override public DistributionStatusEnum getStatus() {
401                     return status;
402                 }
403
404                 @Override public String getComponentName() {
405                     return sdcConfig.getUser();
406                 }
407             };
408
409             if (errorReason != null) {
410                 this.distributionClient.sendComponentDoneStatus(message, errorReason);
411             } else {
412                 this.distributionClient.sendComponentDoneStatus(message);
413             }
414         } catch (RuntimeException e) {
415             logger.warn("Unable to send the SDC Notification (" + status.name() + ") due to an exception", e);
416         }
417         logger.info("SDC Notification sent successfully(" + status.name() + ")");
418     }
419
420     private void sendDownloadStatus(IDistributionStatusMessage message, String errorReason) {
421         if (errorReason != null) {
422             this.distributionClient.sendDownloadStatus(message, errorReason);
423         } else {
424             this.distributionClient.sendDownloadStatus(message);
425         }
426     }
427
428     private void sendDeploymentStatus(IDistributionStatusMessage message, String errorReason) {
429         if (errorReason != null) {
430             this.distributionClient.sendDeploymentStatus(message, errorReason);
431         } else {
432             this.distributionClient.sendDeploymentStatus(message);
433         }
434     }
435 }