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