Checkstyle improvements
[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.exception.sdc.controller.SdcParametersException;
42 import org.onap.clamp.clds.sdc.controller.installer.BlueprintArtifact;
43 import org.onap.clamp.clds.sdc.controller.installer.CsarHandler;
44 import org.onap.clamp.clds.sdc.controller.installer.CsarInstaller;
45 import org.onap.clamp.clds.util.LoggingUtils;
46 import org.onap.sdc.api.IDistributionClient;
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 cannot be instantiated or if
177      *                                an init attempt is done when already initialized
178      */
179     public void initSdc() throws SdcControllerException {
180         logger.info("Attempt to initialize the SDC Controller: " + sdcConfig.getSdcControllerName());
181         if (this.getControllerStatus() != SdcSingleControllerStatus.STOPPED) {
182             throw new SdcControllerException("The controller is already initialized, call the closeSDC method first");
183         }
184         if (distributionClient == null) {
185             distributionClient = DistributionClientFactory.createDistributionClient();
186         }
187         IDistributionClientResult result = distributionClient.init(sdcConfig,
188                 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(
193                     "Initialization of the SDC Controller failed with reason: "
194                             + result.getDistributionMessageResult());
195         }
196         logger.info("SDC Controller successfully initialized: " + sdcConfig.getSdcControllerName());
197         logger.info("Attempt to start the SDC Controller: " + sdcConfig.getSdcControllerName());
198         result = this.distributionClient.start();
199         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
200             logger.error("SDC distribution client start failed with reason:" + result.getDistributionMessageResult());
201             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);
202             throw new SdcControllerException(
203                     "Startup of the SDC Controller failed with reason: " + result.getDistributionMessageResult());
204         }
205         logger.info("SDC Controller successfully started: " + sdcConfig.getSdcControllerName());
206         this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
207     }
208
209     /**
210     * This method closes the SDC Controller and the SDC Client.
211     *
212     * @throws SdcControllerException It throws an exception if the SDC Client cannot be closed because
213     *                                it's currently BUSY 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,
249                         System.currentTimeMillis());
250             }
251         } else {
252             this.sendSdcNotification(notificationType, null, sdcConfig.getConsumerID(),
253                     notificationData.getDistributionID(),
254                     distributionStatus, errorMessage, System.currentTimeMillis());
255         }
256     }
257
258     /**
259      * This method processes the notification received from Sdc.
260      *
261      * @param notificationData The INotificationData
262      */
263     public void treatNotification(INotificationData notificationData) {
264         CsarHandler csar = null;
265         try {
266             // wait for a random time, so that 2 running Clamp will not treat
267             // the same Notification at the same time
268             Thread.sleep(ThreadLocalRandom.current().nextInt(1, 10) * 1000L);
269             logger.info("Notification received for service UUID:" + notificationData.getServiceUUID());
270             this.changeControllerStatus(SdcSingleControllerStatus.BUSY);
271             csar = new CsarHandler(notificationData, this.sdcConfig.getSdcControllerName(),
272                     refProp.getStringValue(CONFIG_SDC_FOLDER));
273             csar.save(downloadTheArtifact(csar.getArtifactElement()));
274             if (csarInstaller.isCsarAlreadyDeployed(csar)) {
275                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
276                         DistributionStatusEnum.ALREADY_DOWNLOADED, null);
277                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
278                         DistributionStatusEnum.ALREADY_DEPLOYED, null);
279             } else {
280                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DOWNLOAD,
281                         DistributionStatusEnum.DOWNLOAD_OK, null);
282                 csarInstaller.installTheCsar(csar);
283                 sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
284                         DistributionStatusEnum.DEPLOY_OK, null);
285             }
286         } catch (SdcArtifactInstallerException | SdcToscaParserException e) {
287             logger.error("SdcArtifactInstallerException exception caught during the notification processing", e);
288             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
289                     DistributionStatusEnum.DEPLOY_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         } catch (PolicyModelException e) {
295             logger.error("PolicyModelException exception caught during the notification processing", e);
296             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
297                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
298         } catch (InterruptedException e) {
299             logger.error("Interrupt exception caught during the notification processing", e);
300             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
301                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
302             Thread.currentThread().interrupt();
303         } catch (RuntimeException e) {
304             logger.error("Unexpected exception caught during the notification processing", e);
305             sendAllNotificationForCsarHandler(notificationData, csar, NotificationType.DEPLOY,
306                     DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());
307         }  finally {
308             this.changeControllerStatus(SdcSingleControllerStatus.IDLE);
309         }
310     }
311
312     private enum NotificationType {
313         /**
314          * Download notification type.
315          */
316         DOWNLOAD,
317         /**
318          * Deploy notification type.
319          */
320         DEPLOY
321     }
322
323     private IDistributionClientDownloadResult downloadTheArtifact(IArtifactInfo artifact) throws SdcDownloadException {
324         logger.info(
325                 "Trying to download the artifact : " + artifact.getArtifactURL() + " UUID: "
326                         + artifact.getArtifactUUID());
327         IDistributionClientDownloadResult downloadResult;
328         try {
329             downloadResult = distributionClient.download(artifact);
330             if (null == downloadResult) {
331                 logger.info("downloadResult is Null for: " + artifact.getArtifactUUID());
332                 return null;
333             }
334         } catch (RuntimeException e) {
335             throw new SdcDownloadException("Exception caught when downloading the artifact", e);
336         }
337         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
338             logger.info("Successfully downloaded the artifact " + artifact.getArtifactURL() + " UUID "
339                     + artifact.getArtifactUUID() + "Size of payload " + downloadResult.getArtifactPayload().length);
340         } else {
341             throw new SdcDownloadException(
342                     "Artifact " + artifact.getArtifactName() + " could not be downloaded from SDC URL "
343                             + artifact.getArtifactURL() + " UUID " + artifact.getArtifactUUID() + ")"
344                             + System.lineSeparator()
345                             + "Error message is " + downloadResult.getDistributionMessageResult()
346                             + System.lineSeparator());
347         }
348         return downloadResult;
349     }
350
351     private void sendSdcNotification(NotificationType notificationType, String artifactUrl, String consumerID,
352                                      String distributionID, DistributionStatusEnum status, String errorReason,
353                                      long timestamp) {
354         String event = "Sending " + notificationType.name() + "(" + status.name() + ")"
355                 + " notification to SDC for artifact:" + artifactUrl;
356         if (errorReason != null) {
357             event = event + "(" + errorReason + ")";
358         }
359         logger.info(event);
360         String action = "";
361         try {
362             IDistributionStatusMessage message = new DistributionStatusMessage(artifactUrl, consumerID, distributionID,
363                     status, timestamp);
364             switch (notificationType) {
365                 case DOWNLOAD:
366                     this.sendDownloadStatus(message, errorReason);
367                     action = "sendDownloadStatus";
368                     break;
369                 case DEPLOY:
370                     this.sendDeploymentStatus(message, errorReason);
371                     action = "sendDeploymentdStatus";
372                     break;
373                 default:
374                     break;
375             }
376         } catch (RuntimeException e) {
377             logger.warn("Unable to send the SDC Notification (" + action + ") due to an exception", e);
378         }
379         logger.info("SDC Notification sent successfully(" + action + ")");
380     }
381
382     private void sendDownloadStatus(IDistributionStatusMessage message, String errorReason) {
383         if (errorReason != null) {
384             this.distributionClient.sendDownloadStatus(message, errorReason);
385         } else {
386             this.distributionClient.sendDownloadStatus(message);
387         }
388     }
389
390     private void sendDeploymentStatus(IDistributionStatusMessage message, String errorReason) {
391         if (errorReason != null) {
392             this.distributionClient.sendDeploymentStatus(message, errorReason);
393         } else {
394             this.distributionClient.sendDeploymentStatus(message);
395         }
396     }
397 }