Merge "Improve tests"
[clamp.git] / src / main / java / org / onap / clamp / clds / sdc / controller / SdcSingleController.java
1 /*-\r
2  * ============LICENSE_START=======================================================\r
3  * ONAP CLAMP\r
4  * ================================================================================\r
5  * Copyright (C) 2018 AT&T Intellectual Property. All rights\r
6  *                             reserved.\r
7  * ================================================================================\r
8  * Licensed under the Apache License, Version 2.0 (the "License");\r
9  * you may not use this file except in compliance with the License.\r
10  * You may obtain a copy of the License at\r
11  *\r
12  * http://www.apache.org/licenses/LICENSE-2.0\r
13  *\r
14  * Unless required by applicable law or agreed to in writing, software\r
15  * distributed under the License is distributed on an "AS IS" BASIS,\r
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
17  * See the License for the specific language governing permissions and\r
18  * limitations under the License.\r
19  * ============LICENSE_END============================================\r
20  * Modifications copyright (c) 2018 Nokia\r
21  * ===================================================================\r
22  *\r
23  */\r
24 \r
25 package org.onap.clamp.clds.sdc.controller;\r
26 \r
27 import com.att.eelf.configuration.EELFLogger;\r
28 import com.att.eelf.configuration.EELFManager;\r
29 \r
30 import java.util.Date;\r
31 import java.util.Map.Entry;\r
32 import java.util.concurrent.ThreadLocalRandom;\r
33 \r
34 import org.onap.clamp.clds.config.ClampProperties;\r
35 import org.onap.clamp.clds.config.sdc.SdcSingleControllerConfiguration;\r
36 import org.onap.clamp.clds.exception.policy.PolicyModelException;\r
37 import org.onap.clamp.clds.exception.sdc.controller.CsarHandlerException;\r
38 import org.onap.clamp.clds.exception.sdc.controller.SdcArtifactInstallerException;\r
39 import org.onap.clamp.clds.exception.sdc.controller.SdcControllerException;\r
40 import org.onap.clamp.clds.exception.sdc.controller.SdcDownloadException;\r
41 import org.onap.clamp.clds.exception.sdc.controller.SdcParametersException;\r
42 import org.onap.clamp.clds.sdc.controller.installer.BlueprintArtifact;\r
43 import org.onap.clamp.clds.sdc.controller.installer.CsarHandler;\r
44 import org.onap.clamp.clds.sdc.controller.installer.CsarInstaller;\r
45 import org.onap.clamp.clds.util.LoggingUtils;\r
46 import org.onap.sdc.api.IDistributionClient;\r
47 import org.onap.sdc.api.consumer.IDistributionStatusMessage;\r
48 import org.onap.sdc.api.consumer.INotificationCallback;\r
49 import org.onap.sdc.api.notification.IArtifactInfo;\r
50 import org.onap.sdc.api.notification.INotificationData;\r
51 import org.onap.sdc.api.results.IDistributionClientDownloadResult;\r
52 import org.onap.sdc.api.results.IDistributionClientResult;\r
53 import org.onap.sdc.impl.DistributionClientFactory;\r
54 import org.onap.sdc.tosca.parser.exceptions.SdcToscaParserException;\r
55 import org.onap.sdc.utils.DistributionActionResultEnum;\r
56 import org.onap.sdc.utils.DistributionStatusEnum;\r
57 \r
58 /**\r
59  * This class handles one sdc controller defined in the config.\r
60  */\r
61 public class SdcSingleController {\r
62 \r
63     private static final EELFLogger logger = EELFManager.getInstance().getLogger(SdcSingleController.class);\r
64     private boolean isSdcClientAutoManaged = false;\r
65     private CsarInstaller csarInstaller;\r
66     private ClampProperties refProp;\r
67     public static final String CONFIG_SDC_FOLDER = "sdc.csarFolder";\r
68     private int nbOfNotificationsOngoing = 0;\r
69     private SdcSingleControllerStatus controllerStatus = SdcSingleControllerStatus.STOPPED;\r
70     private SdcSingleControllerConfiguration sdcConfig;\r
71     private IDistributionClient distributionClient;\r
72 \r
73     /**\r
74      * Inner class for Notification callback\r
75      */\r
76     private final class SdcNotificationCallBack implements INotificationCallback {\r
77 \r
78         private SdcSingleController sdcController;\r
79 \r
80         SdcNotificationCallBack(SdcSingleController controller) {\r
81             sdcController = controller;\r
82         }\r
83 \r
84         /**\r
85          * This method can be called multiple times at the same moment. The controller\r
86          * must be thread safe !\r
87          */\r
88         @Override\r
89         public void activateCallback(INotificationData iNotif) {\r
90             Date startTime = new Date();\r
91             logger.info("Receive a callback notification in SDC, nb of resources: " + iNotif.getResources().size());\r
92             sdcController.treatNotification(iNotif);\r
93             LoggingUtils.setTimeContext(startTime, new Date());\r
94             LoggingUtils.setResponseContext("0", "SDC Notification received and processed successfully",\r
95                 this.getClass().getName());\r
96         }\r
97     }\r
98 \r
99     public int getNbOfNotificationsOngoing() {\r
100         return nbOfNotificationsOngoing;\r
101     }\r
102 \r
103     private void changeControllerStatusIdle() {\r
104         if (this.nbOfNotificationsOngoing > 1) {\r
105             --this.nbOfNotificationsOngoing;\r
106         } else {\r
107             this.nbOfNotificationsOngoing = 0;\r
108             this.controllerStatus = SdcSingleControllerStatus.IDLE;\r
109         }\r
110     }\r
111 \r
112     protected final synchronized void changeControllerStatus(SdcSingleControllerStatus newControllerStatus) {\r
113         switch (newControllerStatus) {\r
114         case BUSY:\r
115             ++this.nbOfNotificationsOngoing;\r
116             this.controllerStatus = newControllerStatus;\r
117             break;\r
118         case IDLE:\r
119             this.changeControllerStatusIdle();\r
120             break;\r
121         default:\r
122             this.controllerStatus = newControllerStatus;\r
123             break;\r
124         }\r
125     }\r
126 \r
127     public final synchronized SdcSingleControllerStatus getControllerStatus() {\r
128         return this.controllerStatus;\r
129     }\r
130 \r
131     public SdcSingleController(ClampProperties clampProp, CsarInstaller csarInstaller,\r
132         SdcSingleControllerConfiguration sdcSingleConfig, IDistributionClient distributionClient) {\r
133         this.distributionClient = distributionClient;\r
134         isSdcClientAutoManaged = (distributionClient == null);\r
135         this.sdcConfig = sdcSingleConfig;\r
136         this.refProp = clampProp;\r
137         this.csarInstaller = csarInstaller;\r
138     }\r
139 \r
140     /**\r
141      * This method initializes the SDC Controller and the SDC Client.\r
142      *\r
143      * @throws SdcControllerException\r
144      *         It throws an exception if the SDC Client cannot be instantiated or if\r
145      *         an init attempt is done when already initialized\r
146      * @throws SdcParametersException\r
147      *         If there is an issue with the parameters provided\r
148      */\r
149     public void initSdc() throws SdcControllerException {\r
150         logger.info("Attempt to initialize the SDC Controller: " + sdcConfig.getSdcControllerName());\r
151         if (this.getControllerStatus() != SdcSingleControllerStatus.STOPPED) {\r
152             throw new SdcControllerException("The controller is already initialized, call the closeSDC method first");\r
153         }\r
154         if (distributionClient == null) {\r
155             distributionClient = DistributionClientFactory.createDistributionClient();\r
156         }\r
157         IDistributionClientResult result = distributionClient.init(sdcConfig, new SdcNotificationCallBack(this));\r
158         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {\r
159             logger.error("SDC distribution client init failed with reason:" + result.getDistributionMessageResult());\r
160             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);\r
161             throw new SdcControllerException(\r
162                 "Initialization of the SDC Controller failed with reason: " + result.getDistributionMessageResult());\r
163         }\r
164         logger.info("SDC Controller successfully initialized: " + sdcConfig.getSdcControllerName());\r
165         logger.info("Attempt to start the SDC Controller: " + sdcConfig.getSdcControllerName());\r
166         result = this.distributionClient.start();\r
167         if (!result.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {\r
168             logger.error("SDC distribution client start failed with reason:" + result.getDistributionMessageResult());\r
169             this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);\r
170             throw new SdcControllerException(\r
171                 "Startup of the SDC Controller failed with reason: " + result.getDistributionMessageResult());\r
172         }\r
173         logger.info("SDC Controller successfully started: " + sdcConfig.getSdcControllerName());\r
174         this.changeControllerStatus(SdcSingleControllerStatus.IDLE);\r
175     }\r
176 \r
177     /**\r
178      * This method closes the SDC Controller and the SDC Client.\r
179      *\r
180      * @throws SdcControllerException\r
181      *         It throws an exception if the SDC Client cannot be closed because\r
182      *         it's currently BUSY in processing notifications.\r
183      */\r
184     public void closeSdc() throws SdcControllerException {\r
185         if (this.getControllerStatus() == SdcSingleControllerStatus.BUSY) {\r
186             throw new SdcControllerException("Cannot close the SDC controller as it's currently in BUSY state");\r
187         }\r
188         if (this.distributionClient != null) {\r
189             this.distributionClient.stop();\r
190             // If auto managed we can set it to Null, SdcController controls it.\r
191             // In the other case the client of this class has specified it, so\r
192             // we can't reset it\r
193             if (isSdcClientAutoManaged) {\r
194                 // Next init will initialize it with a new SDC Client\r
195                 this.distributionClient = null;\r
196             }\r
197         }\r
198         this.changeControllerStatus(SdcSingleControllerStatus.STOPPED);\r
199     }\r
200 \r
201     private void sendAllNotificationForCsarHandler(INotificationData iNotif, CsarHandler csar,\r
202         NotificationType notificationType, DistributionStatusEnum distributionStatus, String errorMessage) {\r
203         if (csar != null) {\r
204             // Notify for the CSAR\r
205             this.sendSdcNotification(notificationType, csar.getArtifactElement().getArtifactURL(),\r
206                 sdcConfig.getConsumerID(), iNotif.getDistributionID(), distributionStatus, errorMessage,\r
207                 System.currentTimeMillis());\r
208             // Notify for all VF resources found\r
209             for (Entry<String, BlueprintArtifact> blueprint : csar.getMapOfBlueprints().entrySet()) {\r
210                 // Normally always 1 artifact in resource for Clamp as we\r
211                 // specified\r
212                 // only VF_METADATA type\r
213                 this.sendSdcNotification(notificationType,\r
214                     blueprint.getValue().getResourceAttached().getArtifacts().get(0).getArtifactURL(),\r
215                     sdcConfig.getConsumerID(), iNotif.getDistributionID(), distributionStatus, errorMessage,\r
216                     System.currentTimeMillis());\r
217             }\r
218         } else {\r
219             this.sendSdcNotification(notificationType, null, sdcConfig.getConsumerID(), iNotif.getDistributionID(),\r
220                 distributionStatus, errorMessage, System.currentTimeMillis());\r
221         }\r
222     }\r
223 \r
224     /**\r
225      * This method processes the notification received from Sdc.\r
226      *\r
227      * @param iNotif\r
228      *        The INotificationData\r
229      */\r
230     public void treatNotification(INotificationData iNotif) {\r
231         CsarHandler csar = null;\r
232         try {\r
233             // wait for a random time, so that 2 running Clamp will not treat\r
234             // the same Notification at the same time\r
235             Thread.sleep(ThreadLocalRandom.current().nextInt(1, 10) * 1000L);\r
236             logger.info("Notification received for service UUID:" + iNotif.getServiceUUID());\r
237             this.changeControllerStatus(SdcSingleControllerStatus.BUSY);\r
238             csar = new CsarHandler(iNotif, this.sdcConfig.getSdcControllerName(),\r
239                 refProp.getStringValue(CONFIG_SDC_FOLDER));\r
240             csar.save(downloadTheArtifact(csar.getArtifactElement()));\r
241             if (csarInstaller.isCsarAlreadyDeployed(csar)) {\r
242                 sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DOWNLOAD,\r
243                     DistributionStatusEnum.ALREADY_DOWNLOADED, null);\r
244                 sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
245                     DistributionStatusEnum.ALREADY_DEPLOYED, null);\r
246             } else {\r
247                 sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DOWNLOAD,\r
248                     DistributionStatusEnum.DOWNLOAD_OK, null);\r
249                 csarInstaller.installTheCsar(csar);\r
250                 sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
251                     DistributionStatusEnum.DEPLOY_OK, null);\r
252             }\r
253         } catch (SdcArtifactInstallerException | SdcToscaParserException e) {\r
254             logger.error("SdcArtifactInstallerException exception caught during the notification processing", e);\r
255             sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
256                 DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());\r
257         } catch (SdcDownloadException | CsarHandlerException e) {\r
258             logger.error("SdcDownloadException exception caught during the notification processing", e);\r
259             sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DOWNLOAD,\r
260                 DistributionStatusEnum.DOWNLOAD_ERROR, e.getMessage());\r
261         } catch (PolicyModelException e) {\r
262             logger.error("PolicyModelException exception caught during the notification processing", e);\r
263             sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
264                 DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());\r
265         } catch (InterruptedException e) {\r
266             logger.error("Interrupt exception caught during the notification processing", e);\r
267             sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
268                 DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());\r
269             Thread.currentThread().interrupt();\r
270         } catch (RuntimeException e) {\r
271             logger.error("Unexpected exception caught during the notification processing", e);\r
272             sendAllNotificationForCsarHandler(iNotif, csar, NotificationType.DEPLOY,\r
273                 DistributionStatusEnum.DEPLOY_ERROR, e.getMessage());\r
274         }  finally {\r
275             this.changeControllerStatus(SdcSingleControllerStatus.IDLE);\r
276         }\r
277     }\r
278 \r
279     private enum NotificationType {\r
280         DOWNLOAD, DEPLOY\r
281     }\r
282 \r
283     private IDistributionClientDownloadResult downloadTheArtifact(IArtifactInfo artifact) throws SdcDownloadException {\r
284         logger.info(\r
285             "Trying to download the artifact : " + artifact.getArtifactURL() + " UUID: " + artifact.getArtifactUUID());\r
286         IDistributionClientDownloadResult downloadResult;\r
287         try {\r
288             downloadResult = distributionClient.download(artifact);\r
289             if (null == downloadResult) {\r
290                 logger.info("downloadResult is Null for: " + artifact.getArtifactUUID());\r
291                 return null;\r
292             }\r
293         } catch (RuntimeException e) {\r
294             throw new SdcDownloadException("Exception caught when downloading the artifact", e);\r
295         }\r
296         if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {\r
297             logger.info("Successfully downloaded the artifact " + artifact.getArtifactURL() + " UUID "\r
298                 + artifact.getArtifactUUID() + "Size of payload " + downloadResult.getArtifactPayload().length);\r
299         } else {\r
300             throw new SdcDownloadException(\r
301                 "Artifact " + artifact.getArtifactName() + " could not be downloaded from SDC URL "\r
302                     + artifact.getArtifactURL() + " UUID " + artifact.getArtifactUUID() + ")" + System.lineSeparator()\r
303                     + "Error message is " + downloadResult.getDistributionMessageResult() + System.lineSeparator());\r
304         }\r
305         return downloadResult;\r
306     }\r
307 \r
308     private void sendSdcNotification(NotificationType notificationType, String artifactURL, String consumerID,\r
309         String distributionID, DistributionStatusEnum status, String errorReason, long timestamp) {\r
310         String event = "Sending " + notificationType.name() + "(" + status.name() + ")"\r
311             + " notification to SDC for artifact:" + artifactURL;\r
312         if (errorReason != null) {\r
313             event = event + "(" + errorReason + ")";\r
314         }\r
315         logger.info(event);\r
316         String action = "";\r
317         try {\r
318             IDistributionStatusMessage message = new DistributionStatusMessage(artifactURL, consumerID, distributionID,\r
319                 status, timestamp);\r
320             switch (notificationType) {\r
321             case DOWNLOAD:\r
322                 this.sendDownloadStatus(message, errorReason);\r
323                 action = "sendDownloadStatus";\r
324                 break;\r
325             case DEPLOY:\r
326                 this.sendDeploymentStatus(message, errorReason);\r
327                 action = "sendDeploymentdStatus";\r
328                 break;\r
329             default:\r
330                 break;\r
331             }\r
332         } catch (RuntimeException e) {\r
333             logger.warn("Unable to send the SDC Notification (" + action + ") due to an exception", e);\r
334         }\r
335         logger.info("SDC Notification sent successfully(" + action + ")");\r
336     }\r
337 \r
338     private void sendDownloadStatus(IDistributionStatusMessage message, String errorReason) {\r
339         if (errorReason != null) {\r
340             this.distributionClient.sendDownloadStatus(message, errorReason);\r
341         } else {\r
342             this.distributionClient.sendDownloadStatus(message);\r
343         }\r
344     }\r
345 \r
346     private void sendDeploymentStatus(IDistributionStatusMessage message, String errorReason) {\r
347         if (errorReason != null) {\r
348             this.distributionClient.sendDeploymentStatus(message, errorReason);\r
349         } else {\r
350             this.distributionClient.sendDeploymentStatus(message);\r
351         }\r
352     }\r
353 }\r