6b8f3b618db425eb97058e84fde22e76142b00ff
[multicloud/framework.git] / artifactbroker / plugins / reception-plugins / src / main / java / org / onap / policy / distribution / reception / handling / sdc / SdcReceptionHandler.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2018 Ericsson. All rights reserved.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.distribution.reception.handling.sdc;
22
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.HashMap;
33
34 import org.onap.policy.common.logging.flexlogger.FlexLogger;
35 import org.onap.policy.common.logging.flexlogger.Logger;
36 import org.onap.policy.common.parameters.ParameterService;
37 import org.onap.policy.distribution.model.CloudArtifact;
38 import org.onap.policy.distribution.model.Csar;
39 import org.onap.policy.distribution.model.GsonUtil;
40 import org.onap.policy.distribution.model.VfModuleModel;
41 import org.onap.policy.distribution.reception.decoding.PolicyDecodingException;
42 import org.onap.policy.distribution.reception.handling.AbstractReceptionHandler;
43 import org.onap.policy.distribution.reception.handling.sdc.SdcClientHandler.SdcClientOperationType;
44 import org.onap.policy.distribution.reception.handling.sdc.exceptions.ArtifactDownloadException;
45 import org.onap.policy.distribution.reception.statistics.DistributionStatisticsManager;
46 import org.onap.sdc.api.IDistributionClient;
47 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
48 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
49 import org.onap.sdc.api.consumer.INotificationCallback;
50 import org.onap.sdc.api.notification.IArtifactInfo;
51 import org.onap.sdc.api.notification.INotificationData;
52 import org.onap.sdc.api.notification.IResourceInstance;
53 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
54 import org.onap.sdc.api.results.IDistributionClientResult;
55 import org.onap.sdc.impl.DistributionClientFactory;
56 import org.onap.sdc.utils.DistributionActionResultEnum;
57 import org.onap.sdc.utils.DistributionStatusEnum;
58
59 /**
60  * Handles reception of inputs from ONAP Service Design and Creation (SDC) from which policies may be decoded.
61  */
62 public class SdcReceptionHandler extends AbstractReceptionHandler implements INotificationCallback {
63
64     private static final Logger LOGGER = FlexLogger.getLogger(SdcReceptionHandler.class);
65     private static final String SECONDS = "Seconds";
66
67     private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
68     private IDistributionClient distributionClient;
69     private SdcConfiguration sdcConfig;
70     private volatile int nbOfNotificationsOngoing = 0;
71     private int retryDelay;
72     private SdcClientHandler sdcClientHandler;
73
74     private enum DistributionStatusType {
75         DOWNLOAD, DEPLOY
76     }
77
78     @Override
79     protected void initializeReception(final String parameterGroupName) {
80         final SdcReceptionHandlerConfigurationParameterGroup handlerParameters =
81                 ParameterService.get(parameterGroupName);
82         retryDelay = handlerParameters.getRetryDelay() < 30 ? 30 : handlerParameters.getRetryDelay();
83         sdcConfig = new SdcConfiguration(handlerParameters);
84         distributionClient = createSdcDistributionClient();
85         sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.START, retryDelay);
86     }
87
88     @Override
89     public void destroy() {
90         if (distributionClient != null) {
91             sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.STOP, retryDelay);
92         }
93     }
94
95     @Override
96     public void activateCallback(final INotificationData notificationData) {
97         LOGGER.debug("Receieved the notification from SDC with ID: " + notificationData.getDistributionID());
98         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.BUSY);
99         //Process only the resource artifacts in MC
100         for (IResourceInstance resource : notificationData.getResources()) {
101             // We process only VNF resource in MC
102             if ("VF".equals(resource.getResourceType())) {
103                 this.processVfModulesArtifacts(notificationData,resource);
104             }
105         }
106         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
107         LOGGER.debug("Processed the notification from SDC with ID: " + notificationData.getDistributionID());
108     }
109
110     /**
111      * Method to change the status of this reception handler instance.
112      *
113      * @param newStatus the new status
114      */
115     private final synchronized void changeSdcReceptionHandlerStatus(final SdcReceptionHandlerStatus newStatus) {
116         switch (newStatus) {
117             case INIT:
118             case STOPPED:
119                 sdcReceptionHandlerStatus = newStatus;
120                 break;
121             case IDLE:
122                 handleIdleStatusChange(newStatus);
123                 break;
124             case BUSY:
125                 ++nbOfNotificationsOngoing;
126                 sdcReceptionHandlerStatus = newStatus;
127                 break;
128             default:
129                 break;
130         }
131     }
132
133     /**
134      * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
135      *
136      * @return the {@link IDistributionClient} instance
137      */
138     protected IDistributionClient createSdcDistributionClient() {
139         return DistributionClientFactory.createDistributionClient();
140     }
141
142     /**
143      * Method to initialize the SDC client.
144      *
145      */
146     protected void initializeSdcClient() {
147
148         LOGGER.debug("Initializing the SDC Client...");
149         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
150             LOGGER.error("The SDC Client is already initialized");
151             return;
152         }
153         final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
154         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
155             LOGGER.error("SDC client initialization failed with reason:" + clientResult.getDistributionMessageResult()
156                     + ". Initialization will be retried after " + retryDelay + SECONDS);
157             return;
158         }
159         LOGGER.debug("SDC Client is initialized successfully");
160         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
161     }
162
163     /**
164      * Method to start the SDC client.
165      *
166      */
167     protected void startSdcClient() {
168
169         LOGGER.debug("Going to start the SDC Client...");
170         if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.INIT) {
171             LOGGER.error("The SDC Client is not initialized");
172             return;
173         }
174         final IDistributionClientResult clientResult = distributionClient.start();
175         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
176             LOGGER.error("SDC client start failed with reason:" + clientResult.getDistributionMessageResult()
177                     + ". Start will be retried after " + retryDelay + SECONDS);
178             return;
179         }
180         LOGGER.debug("SDC Client is started successfully");
181         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
182         sdcClientHandler.cancel();
183     }
184
185     /**
186      * Method to stop the SDC client.
187      *
188      */
189     protected void stopSdcClient() {
190         LOGGER.debug("Going to stop the SDC Client...");
191         final IDistributionClientResult clientResult = distributionClient.stop();
192         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
193             LOGGER.error("SDC client stop failed with reason:" + clientResult.getDistributionMessageResult()
194                     + ". Stop will be retried after " + retryDelay + SECONDS);
195             return;
196         }
197         LOGGER.debug("SDC Client is stopped successfully");
198         changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
199         sdcClientHandler.cancel();
200     }
201
202     /**
203      * Method to process VF MODULES_METADATA artifacts from incoming SDC notification.
204      *
205      * @param artifact the item needs to check if it has been stored in DBB or not
206      */
207     private boolean checkArtifactAlreadyStored(IArtifactInfo artifact) {
208         //TODO
209         return false;
210
211     }
212
213     /**
214      * Method to process VF MODULES_METADATA artifacts from incoming SDC notification.
215      *
216      * @param notificationData the notification from SDC
217      * @param resource every resource of the service from SDC
218      */
219     public void processVfModulesArtifacts(final INotificationData notificationData, IResourceInstance resource) {
220         boolean artifactsProcessedSuccessfully = true;
221         DistributionStatisticsManager.updateTotalDistributionCount();
222         List<String> relevantArtifactTypes = sdcConfig.getRelevantArtifactTypes();
223         Path path = Paths.get("/data");
224         ArrayList<VfModuleModel> vfModuleModels = new ArrayList<>();
225         HashMap<String, IArtifactInfo> artifactMap = new HashMap<>();//key is UUID, value is artifact for shared folder
226         String vfArtifactData = null;
227
228         for (final IArtifactInfo artifact : resource.getArtifacts()) {
229             artifactMap.put(artifact.getArtifactUUID(),artifact);
230
231              //extract the artifactlist and write them into MongoDB
232             if (artifact.getArtifactType().equals("VF_MODULES_METADATA")) {
233                 try {
234                     final IDistributionClientDownloadResult resultArtifact =
235                             downloadTheArtifact(artifact,notificationData);
236                     vfArtifactData = new String(resultArtifact.getArtifactPayload());
237                     vfModuleModels= GsonUtil.parseJsonArrayWithGson(vfArtifactData,VfModuleModel.class);
238                 } catch (final ArtifactDownloadException exp) {
239                     LOGGER.error("Failed to process csar service artifacts ", exp);
240                     artifactsProcessedSuccessfully = false;
241                     sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
242                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
243                         "Failed to deploy the artifact due to: " + exp.getMessage());
244                 }
245             }
246         }
247
248         //foreach(vf_module_metadata)
249         //  1. create a  dir like /data/UUID1
250         //  2. put the vfmodule-meta.json into it
251         //  3. put the service-meta.json into it
252         //  3. go through each aritfact uuid under artifact_list of vf_module and download
253         for (final VfModuleModel vfModule : vfModuleModels) {
254             try {
255                 //create the new dir
256                 Path temp = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID());
257                 path = Files.createDirectory(temp);//create UUID path
258                 //store the value to vfmodule-meta.json
259                 String filePath = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID(),"vfmodule-meta.json").toString();
260                 writeFileByFileWriter(filePath, vfArtifactData);
261                 //store the service level info to serivce-meta.json
262                 filePath = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID(),"service-meta.json").toString();
263                 writeFileByFileWriter(filePath, notificationData.toString());
264             } catch (final IOException exp) {
265                 LOGGER.error("Failed to create  directory artifact file", exp);
266             }
267
268             for(final String uuid : vfModule.getArtifacts()) {
269                 try {
270                     IArtifactInfo artifact = artifactMap.get(uuid);
271                     final IDistributionClientDownloadResult resultArtifact = downloadTheArtifact(artifact, notificationData);
272                     writeArtifactToDir(artifact,resultArtifact,path);
273                 } catch (final ArtifactDownloadException exp) {
274                     LOGGER.error("Failed to process csar service artifacts ", exp);
275                     artifactsProcessedSuccessfully = false;
276                     sendDistributionStatus(DistributionStatusType.DEPLOY, artifactMap.get(uuid).getArtifactURL(),
277                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
278                         "Failed to deploy the artifact due to: " + exp.getMessage());
279                 }
280             }
281         }
282
283         //for subplug work
284         try {
285             final CloudArtifact cloudArtifact = new CloudArtifact(vfModuleModels, artifactMap);
286             inputReceived(cloudArtifact);
287         } catch ( final PolicyDecodingException exp) {
288             LOGGER.error("Failed to process cloud  artifacts ", exp);
289             artifactsProcessedSuccessfully = false;
290         }
291
292         if (artifactsProcessedSuccessfully) {
293             DistributionStatisticsManager.updateDistributionSuccessCount();
294             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
295                     null);
296         } else {
297             DistributionStatisticsManager.updateDistributionFailureCount();
298             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
299                     "Failed to process the artifact");
300         }
301     }
302
303     /**
304      * Method to download the distribution artifact.
305      *
306      * @param artifact the artifact
307      * @return the download result
308      * @throws ArtifactDownloadException if download fails
309      */
310     private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
311             final INotificationData notificationData) throws ArtifactDownloadException {
312
313         DistributionStatisticsManager.updateTotalDownloadCount();
314         final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
315         if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
316             DistributionStatisticsManager.updateDownloadFailureCount();
317             final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
318                     + downloadResult.getDistributionMessageResult();
319             LOGGER.error(message);
320             sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
321                     notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
322             throw new ArtifactDownloadException(message);
323         }
324         DistributionStatisticsManager.updateDownloadSuccessCount();
325         sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
326                 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
327         return downloadResult;
328     }
329
330     /**
331      * Method to write a string to a file
332      * @Param filePath the file's path
333      * @Param content the data to be writen
334      * @throws IOException if error happends
335      */
336     private static void writeFileByFileWriter(String filePath, String content) throws IOException {
337         File file = new File(filePath);
338         synchronized (file) {
339             FileWriter fw = new FileWriter(filePath);
340             fw.write(content);
341             fw.close();
342         }
343     }
344     /**
345      * Method to write the downloaded distribution artifact to local file system with specified dir.
346      *
347      * @param artifact the notification artifact
348      * @param resultArtifact the download result artifact
349      * @return the local path of written file
350      * @throws ArtifactDownloadException if error occurs while writing the artifact
351      */
352     private Path writeArtifactToDir(final IArtifactInfo artifact,
353             final IDistributionClientDownloadResult resultArtifact, Path path) throws ArtifactDownloadException {
354         try {
355             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
356             //final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
357             File tempArtifactFile = new File(path.toString() + "/" + artifact.getArtifactName());
358             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
359                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
360                 return tempArtifactFile.toPath();
361             }
362         } catch (final Exception exp) {
363             final String message = "Failed to write artifact to local repository";
364             LOGGER.error(message, exp);
365             throw new ArtifactDownloadException(message, exp);
366         }
367     }
368
369     /**
370      * Method to write the downloaded distribution artifact to local file system.
371      *
372      * @param artifact the notification artifact
373      * @param resultArtifact the download result artifact
374      * @return the local path of written file
375      * @throws ArtifactDownloadException if error occurs while writing the artifact
376      */
377     private Path writeArtifactToFile(final IArtifactInfo artifact,
378             final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
379         try {
380             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
381             final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
382             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
383                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
384                 return tempArtifactFile.toPath();
385             }
386         } catch (final Exception exp) {
387             final String message = "Failed to write artifact to local repository";
388             LOGGER.error(message, exp);
389             throw new ArtifactDownloadException(message, exp);
390         }
391     }
392
393     /**
394      * Method to delete the downloaded notification artifact from local file system.
395      *
396      * @param filePath the path of file
397      */
398     private void deleteArtifactFile(final Path filePath) {
399         try {
400             Files.deleteIfExists(filePath);
401         } catch (final IOException exp) {
402             LOGGER.error("Failed to delete the downloaded artifact file", exp);
403         }
404     }
405
406     /**
407      * Sends the distribution status to SDC using the input values.
408      *
409      * @param statusType the status type
410      * @param artifactUrl the artifact url
411      * @param distributionId the distribution id
412      * @param status the status
413      * @param errorReason the error reason
414      */
415     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
416             final String distributionId, final DistributionStatusEnum status, final String errorReason) {
417
418         IDistributionClientResult clientResult;
419         final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
420                 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
421                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
422         final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
423         if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
424             if (errorReason != null) {
425                 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
426             } else {
427                 clientResult = distributionClient.sendDownloadStatus(message);
428             }
429         } else {
430             if (errorReason != null) {
431                 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
432             } else {
433                 clientResult = distributionClient.sendDeploymentStatus(message);
434             }
435         }
436         final StringBuilder loggerMessage = new StringBuilder();
437         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
438                 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
439                 .append(statusType.name()).append(" Status: ").append(status.name());
440         if (errorReason != null) {
441             loggerMessage.append(" ErrorReason: ").append(errorReason);
442         }
443         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
444             loggerMessage.insert(0, "Failed sending ");
445             LOGGER.debug(loggerMessage);
446         } else {
447             loggerMessage.insert(0, "Successfully Sent ");
448             LOGGER.debug(loggerMessage);
449         }
450     }
451
452     /**
453      * Sends the component done status to SDC using the input values.
454      *
455      * @param distributionId the distribution Id
456      * @param status the distribution status
457      * @param errorReason the error reason
458      */
459     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
460             final String errorReason) {
461         IDistributionClientResult clientResult;
462         final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
463                 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
464                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
465         final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
466         if (errorReason == null) {
467             clientResult = distributionClient.sendComponentDoneStatus(message);
468         } else {
469             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
470         }
471
472         final StringBuilder loggerMessage = new StringBuilder();
473         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
474                 .append(distributionId).append(" Status: ").append(status.name());
475         if (errorReason != null) {
476             loggerMessage.append(" ErrorReason: ").append(errorReason);
477         }
478         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
479             loggerMessage.insert(0, "Failed sending ");
480             LOGGER.debug(loggerMessage);
481         } else {
482             loggerMessage.insert(0, "Successfully Sent ");
483             LOGGER.debug(loggerMessage);
484         }
485     }
486
487     /**
488      * Handle the status change of {@link SdcReceptionHandler} to Idle.
489      *
490      * @param newStatus the new status
491      */
492     private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
493         if (nbOfNotificationsOngoing > 1) {
494             --nbOfNotificationsOngoing;
495         } else {
496             nbOfNotificationsOngoing = 0;
497             sdcReceptionHandlerStatus = newStatus;
498         }
499     }
500 }