fbb37d5256c4a2c6d4ebe2bc9fc889a6870ea968
[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.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, IDistributionClient distributionClient) {
164         this.distributionClient = distributionClient;
165         isSdcClientAutoManaged = (distributionClient == null);
166         this.sdcConfig = sdcSingleConfig;
167         this.refProp = clampProp;
168         this.csarInstaller = csarInstaller;
169     }
170
171     /**
172      * This method initializes the SDC Controller and the SDC Client.
173      *
174      * @throws SdcControllerException It throws an exception if the SDC Client
175      *                                cannot be instantiated or if an init attempt
176      *                                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, new SdcNotificationCallBack(this));
187         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
188             logger.error("SDC distribution client init failed with reason:" + result.getDistributionMessageResult());
189             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
190             throw new SdcControllerException("Initialization of the SDC Controller failed with reason: "
191                     + result.getDistributionMessageResult());
192         }
193         logger.info("SDC Controller successfully initialized: " + sdcConfig.getSdcControllerName());
194         logger.info("Attempt to start the SDC Controller: " + sdcConfig.getSdcControllerName());
195         result = this.distributionClient.start();
196         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
197             logger.error("SDC distribution client start failed with reason:" + result.getDistributionMessageResult());
198             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
199             throw new SdcControllerException(
200                     "Startup of the SDC Controller failed with reason: " + result.getDistributionMessageResult());
201         }
202         logger.info("SDC Controller successfully started: " + sdcConfig.getSdcControllerName());
203         this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
204     }
205
206     /**
207      * This method closes the SDC Controller and the SDC Client.
208      *
209      * @throws SdcControllerException It throws an exception if the SDC Client
210      *                                cannot be closed because it's currently BUSY
211      *                                in processing notifications.
212      */
213     public void closeSdc() throws SdcControllerException {
214         if (this.getControllerStatus() == SdcSingleControllerStatus.BUSY) {
215             throw new SdcControllerException("Cannot close the SDC controller as it's currently in BUSY state");
216         }
217         if (this.distributionClient != null) {
218             this.distributionClient.stop();
219             // If auto managed we can set it to Null, SdcController controls it.
220             // In the other case the client of this class has specified it, so
221             // we can't reset it
222             if (isSdcClientAutoManaged) {
223                 // Next init will initialize it with a new SDC Client
224                 this.distributionClient = null;
225             }
226         }
227         this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
228     }
229
230     private void sendAllNotificationForCsarHandler(INotificationData notificationData, CsarHandler csar,
231             NotificationType notificationType, DistributionStatusEnum distributionStatus, String errorMessage) {
232         if (csar != null) {
233             // Notify for the CSAR
234             this.sendSdcNotification(notificationType, csar.getArtifactElement().getArtifactURL(),
235                     sdcConfig.getConsumerID(), notificationData.getDistributionID(), distributionStatus, errorMessage,
236                     System.currentTimeMillis());
237             // Notify for all VF resources found
238             for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) {
239                 // Normally always 1 artifact in resource for Clamp as we
240                 // specified
241                 // only VF_METADATA type
242                 this.sendSdcNotification(notificationType,
243                         blueprint.getValue().getResourceAttached().getArtifacts().get(0).getArtifactURL(),
244                         sdcConfig.getConsumerID(), notificationData.getDistributionID(), distributionStatus,
245                         errorMessage, System.currentTimeMillis());
246             }
247         } else {
248             this.sendSdcNotification(notificationType, null, sdcConfig.getConsumerID(),
249                     notificationData.getDistributionID(), distributionStatus, errorMessage, System.currentTimeMillis());
250         }
251     }
252
253     /**
254      * This method processes the notification received from Sdc.
255      *
256      * @param notificationData The INotificationData
257      */
258     public void treatNotification(INotificationData notificationData) {
259         CsarHandler csar = null;
260         try {
261             // wait for a random time, so that 2 running Clamp will not treat
262             // the same Notification at the same time
263             Thread.sleep(ThreadLocalRandom.current().nextInt(1, 10) * 1000L);
264             logger.info("Notification received for service UUID:" + notificationData.getServiceUUID());
265             this.changeControllerStatus(SdcSingleControllerStatus.BUSY);
266             csar = new CsarHandler(notificationData, this.sdcConfig.getSdcControllerName(),
267                     refProp.getStringValue(CONFIG_SDC_FOLDER));
268             csar.save(downloadTheArtifact(csar.getArtifactElement()));
269             if (csarInstaller.isCsarAlreadyDeployed(csar)) {
270                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
271                         DistributionStatusEnum.ALREADY_DOWNLOADED, null);
272                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
273                         DistributionStatusEnum.ALREADY_DEPLOYED, null);
274             } else {
275                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
276                         DistributionStatusEnum.DOWNLOAD_OK, null);
277                 csarInstaller.installTheCsar(csar);
278                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
279                         DistributionStatusEnum.DEPLOY_OK, null);
280             }
281         } catch (SdcArtifactInstallerException | SdcToscaParserException e) {
282             logger.error("SdcArtifactInstallerException exception caught during the notification processing", e);
283             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
284                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
285         } catch (SdcDownloadException | CsarHandlerException e) {
286             logger.error("SdcDownloadException exception caught during the notification processing", e);
287             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
288                     DistributionStatusEnum.DOWNLOAD_ERROR, e.getMessage());
289         } catch (InterruptedException e) {
290             logger.error("Interrupt exception caught during the notification processing", e);
291             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
292                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
293             Thread.currentThread().interrupt();
294         } catch (BlueprintParserException e) {
295             logger.error("BlueprintParser exception caught during the notification processing", e);
296             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
297                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
298         } catch (RuntimeException e) {
299             logger.error("Unexpected exception caught during the notification processing", e);
300             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
301                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
302         } finally {
303             this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
304         }
305     }
306
307     private enum NotificationType {
308         /**
309          * Download notification type.
310          */
311         DOWNLOAD,
312         /**
313          * Deploy notification type.
314          */
315         DEPLOY
316     }
317
318     private IDistributionClientDownloadResult downloadTheArtifact(IArtifactInfo artifact) throws SdcDownloadException {
319         logger.info("Trying to download the artifact : " + artifact.getArtifactURL() + " UUID: "
320                 + artifact.getArtifactUUID());
321         IDistributionClientDownloadResult downloadResult;
322         try {
323             downloadResult = distributionClient.download(artifact);
324             if (null == downloadResult) {
325                 logger.info("downloadResult is Null for: " + artifact.getArtifactUUID());
326                 return null;
327             }
328         } catch (RuntimeException e) {
329             throw new SdcDownloadException("Exception caught when downloading the artifact", e);
330         }
331         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
332             logger.info("Successfully downloaded the artifact " + artifact.getArtifactURL() + " UUID "
333                     + artifact.getArtifactUUID() + "Size of payload " + downloadResult.getArtifactPayload().length);
334         } else {
335             throw new SdcDownloadException("Artifact " + artifact.getArtifactName()
336                     + " could not be downloaded from SDC URL " + artifact.getArtifactURL() + " UUID "
337                     + artifact.getArtifactUUID() + ")" + System.lineSeparator() + "Error message is "
338                     + downloadResult.getDistributionMessageResult() + System.lineSeparator());
339         }
340         return downloadResult;
341     }
342
343     private void sendSdcNotification(NotificationType notificationType, String artifactUrl, String consumerId,
344             String distributionId, DistributionStatusEnum status, String errorReason, long timestamp) {
345         String event = "Sending " + notificationType.name() + "(" + status.name() + ")"
346                 + " notification to SDC for artifact:" + artifactUrl;
347         if (errorReason != null) {
348             event = event + "(" + errorReason + ")";
349         }
350         logger.info(event);
351         String action = "";
352         try {
353             IDistributionStatusMessage message = new DistributionStatusMessage(artifactUrl, consumerId, distributionId,
354                     status, timestamp);
355             switch (notificationType) {
356                 case DOWNLOAD:
357                     this.sendDownloadStatus(message, errorReason);
358                     action = "sendDownloadStatus";
359                     break;
360                 case DEPLOY:
361                     this.sendDeploymentStatus(message, errorReason);
362                     action = "sendDeploymentdStatus";
363                     break;
364                 default:
365                     break;
366             }
367         } catch (RuntimeException e) {
368             logger.warn("Unable to send the SDC Notification (" + action + ") due to an exception", e);
369         }
370         logger.info("SDC Notification sent successfully(" + action + ")");
371     }
372
373     private void sendDownloadStatus(IDistributionStatusMessage message, String errorReason) {
374         if (errorReason != null) {
375             this.distributionClient.sendDownloadStatus(message, errorReason);
376         } else {
377             this.distributionClient.sendDownloadStatus(message);
378         }
379     }
380
381     private void sendDeploymentStatus(IDistributionStatusMessage message, String errorReason) {
382         if (errorReason != null) {
383             this.distributionClient.sendDeploymentStatus(message, errorReason);
384         } else {
385             this.distributionClient.sendDeploymentStatus(message);
386         }
387     }
388 }