Fix sonar issue of artifactbroker
[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.HashMap;
32 import java.util.List;
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(),
260                     "vfmodule-meta.json").toString();
261                 writeFileByFileWriter(filePath, vfArtifactData);
262                 //store the service level info to serivce-meta.json
263                 filePath = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID(),
264                     "service-meta.json").toString();
265                 writeFileByFileWriter(filePath, notificationData.toString());
266             } catch (final IOException exp) {
267                 LOGGER.error("Failed to create  directory artifact file", exp);
268             }
269
270             for(final String uuid : vfModule.getArtifacts()) {
271                 try {
272                     IArtifactInfo artifact = artifactMap.get(uuid);
273                     final IDistributionClientDownloadResult resultArtifact = downloadTheArtifact(artifact, notificationData);
274                     writeArtifactToDir(artifact,resultArtifact,path);
275                 } catch (final ArtifactDownloadException exp) {
276                     LOGGER.error("Failed to process csar service artifacts ", exp);
277                     artifactsProcessedSuccessfully = false;
278                     sendDistributionStatus(DistributionStatusType.DEPLOY, artifactMap.get(uuid).getArtifactURL(),
279                         notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
280                         "Failed to deploy the artifact due to: " + exp.getMessage());
281                 }
282             }
283         }
284
285         //for subplug work
286         try {
287             final CloudArtifact cloudArtifact = new CloudArtifact(vfModuleModels, artifactMap);
288             inputReceived(cloudArtifact);
289         } catch ( final PolicyDecodingException exp) {
290             LOGGER.error("Failed to process cloud  artifacts ", exp);
291             artifactsProcessedSuccessfully = false;
292         }
293
294         if (artifactsProcessedSuccessfully) {
295             DistributionStatisticsManager.updateDistributionSuccessCount();
296             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
297                     null);
298         } else {
299             DistributionStatisticsManager.updateDistributionFailureCount();
300             sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
301                     "Failed to process the artifact");
302         }
303     }
304
305     /**
306      * Method to download the distribution artifact.
307      *
308      * @param artifact the artifact
309      * @return the download result
310      * @throws ArtifactDownloadException if download fails
311      */
312     private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
313             final INotificationData notificationData) throws ArtifactDownloadException {
314
315         DistributionStatisticsManager.updateTotalDownloadCount();
316         final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
317         if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
318             DistributionStatisticsManager.updateDownloadFailureCount();
319             final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
320                     + downloadResult.getDistributionMessageResult();
321             LOGGER.error(message);
322             sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
323                     notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
324             throw new ArtifactDownloadException(message);
325         }
326         DistributionStatisticsManager.updateDownloadSuccessCount();
327         sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
328                 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
329         return downloadResult;
330     }
331
332     /**
333      * Method to write a string to a file
334      * @Param filePath the file's path
335      * @Param content the data to be writen
336      * @throws IOException if error happends
337      */
338     private static void writeFileByFileWriter(String filePath, String content) throws IOException {
339         File file = new File(filePath);
340         synchronized (file) {
341             try {
342                 FileWriter fw = new FileWriter(filePath);
343                 fw.write(content);
344                 fw.close();
345             } catch (final IOException exp) {
346                 LOGGER.error("Failed to write File by File Writer ", exp);
347             }
348         }
349     }
350     /**
351      * Method to write the downloaded distribution artifact to local file system with specified dir.
352      *
353      * @param artifact the notification artifact
354      * @param resultArtifact the download result artifact
355      * @return the local path of written file
356      * @throws ArtifactDownloadException if error occurs while writing the artifact
357      */
358     private Path writeArtifactToDir(final IArtifactInfo artifact,
359             final IDistributionClientDownloadResult resultArtifact, Path path) throws ArtifactDownloadException {
360         try {
361             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
362             //final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
363             File tempArtifactFile = new File(path.toString() + "/" + artifact.getArtifactName());
364             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
365                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
366                 return tempArtifactFile.toPath();
367             }
368         } catch (final Exception exp) {
369             final String message = "Failed to write artifact to local repository";
370             LOGGER.error(message, exp);
371             throw new ArtifactDownloadException(message, exp);
372         }
373     }
374
375     /**
376      * Method to write the downloaded distribution artifact to local file system.
377      *
378      * @param artifact the notification artifact
379      * @param resultArtifact the download result artifact
380      * @return the local path of written file
381      * @throws ArtifactDownloadException if error occurs while writing the artifact
382      */
383     private Path writeArtifactToFile(final IArtifactInfo artifact,
384             final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
385         try {
386             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
387             final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
388             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
389                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
390                 return tempArtifactFile.toPath();
391             }
392         } catch (final Exception exp) {
393             final String message = "Failed to write artifact to local repository";
394             LOGGER.error(message, exp);
395             throw new ArtifactDownloadException(message, exp);
396         }
397     }
398
399     /**
400      * Method to delete the downloaded notification artifact from local file system.
401      *
402      * @param filePath the path of file
403      */
404     private void deleteArtifactFile(final Path filePath) {
405         try {
406             Files.deleteIfExists(filePath);
407         } catch (final IOException exp) {
408             LOGGER.error("Failed to delete the downloaded artifact file", exp);
409         }
410     }
411
412     /**
413      * Sends the distribution status to SDC using the input values.
414      *
415      * @param statusType the status type
416      * @param artifactUrl the artifact url
417      * @param distributionId the distribution id
418      * @param status the status
419      * @param errorReason the error reason
420      */
421     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
422             final String distributionId, final DistributionStatusEnum status, final String errorReason) {
423
424         IDistributionClientResult clientResult;
425         final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
426                 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
427                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
428         final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
429         if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
430             if (errorReason != null) {
431                 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
432             } else {
433                 clientResult = distributionClient.sendDownloadStatus(message);
434             }
435         } else {
436             if (errorReason != null) {
437                 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
438             } else {
439                 clientResult = distributionClient.sendDeploymentStatus(message);
440             }
441         }
442         final StringBuilder loggerMessage = new StringBuilder();
443         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
444                 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
445                 .append(statusType.name()).append(" Status: ").append(status.name());
446         if (errorReason != null) {
447             loggerMessage.append(" ErrorReason: ").append(errorReason);
448         }
449         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
450             loggerMessage.insert(0, "Failed sending ");
451             LOGGER.debug(loggerMessage);
452         } else {
453             loggerMessage.insert(0, "Successfully Sent ");
454             LOGGER.debug(loggerMessage);
455         }
456     }
457
458     /**
459      * Sends the component done status to SDC using the input values.
460      *
461      * @param distributionId the distribution Id
462      * @param status the distribution status
463      * @param errorReason the error reason
464      */
465     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
466             final String errorReason) {
467         IDistributionClientResult clientResult;
468         final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
469                 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
470                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
471         final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
472         if (errorReason == null) {
473             clientResult = distributionClient.sendComponentDoneStatus(message);
474         } else {
475             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
476         }
477
478         final StringBuilder loggerMessage = new StringBuilder();
479         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
480                 .append(distributionId).append(" Status: ").append(status.name());
481         if (errorReason != null) {
482             loggerMessage.append(" ErrorReason: ").append(errorReason);
483         }
484         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
485             loggerMessage.insert(0, "Failed sending ");
486             LOGGER.debug(loggerMessage);
487         } else {
488             loggerMessage.insert(0, "Successfully Sent ");
489             LOGGER.debug(loggerMessage);
490         }
491     }
492
493     /**
494      * Handle the status change of {@link SdcReceptionHandler} to Idle.
495      *
496      * @param newStatus the new status
497      */
498     private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
499         if (nbOfNotificationsOngoing > 1) {
500             --nbOfNotificationsOngoing;
501         } else {
502             nbOfNotificationsOngoing = 0;
503             sdcReceptionHandlerStatus = newStatus;
504         }
505     }
506 }