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