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