Fix the sonar issue and clm issue
[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 (FileWriter fw = new FileWriter(filePath)) {
342                 fw.write(content);
343             } catch (final IOException exp) {
344                 LOGGER.error("Failed to write File by File Writer ", exp);
345             }
346         }
347     }
348     /**
349      * Method to write the downloaded distribution artifact to local file system with specified dir.
350      *
351      * @param artifact the notification artifact
352      * @param resultArtifact the download result artifact
353      * @return the local path of written file
354      * @throws ArtifactDownloadException if error occurs while writing the artifact
355      */
356     private Path writeArtifactToDir(final IArtifactInfo artifact,
357             final IDistributionClientDownloadResult resultArtifact, Path path) throws ArtifactDownloadException {
358         try {
359             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
360             //final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
361             File tempArtifactFile = new File(path.toString() + "/" + artifact.getArtifactName());
362             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
363                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
364                 return tempArtifactFile.toPath();
365             }
366         } catch (final Exception exp) {
367             final String message = "Failed to write artifact to local repository";
368             LOGGER.error(message, exp);
369             throw new ArtifactDownloadException(message, exp);
370         }
371     }
372
373     /**
374      * Method to write the downloaded distribution artifact to local file system.
375      *
376      * @param artifact the notification artifact
377      * @param resultArtifact the download result artifact
378      * @return the local path of written file
379      * @throws ArtifactDownloadException if error occurs while writing the artifact
380      */
381     private Path writeArtifactToFile(final IArtifactInfo artifact,
382             final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
383         try {
384             final byte[] payloadBytes = resultArtifact.getArtifactPayload();
385             final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
386             try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
387                 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
388                 return tempArtifactFile.toPath();
389             }
390         } catch (final Exception exp) {
391             final String message = "Failed to write artifact to local repository";
392             LOGGER.error(message, exp);
393             throw new ArtifactDownloadException(message, exp);
394         }
395     }
396
397     /**
398      * Method to delete the downloaded notification artifact from local file system.
399      *
400      * @param filePath the path of file
401      */
402     private void deleteArtifactFile(final Path filePath) {
403         try {
404             Files.deleteIfExists(filePath);
405         } catch (final IOException exp) {
406             LOGGER.error("Failed to delete the downloaded artifact file", exp);
407         }
408     }
409
410     /**
411      * Sends the distribution status to SDC using the input values.
412      *
413      * @param statusType the status type
414      * @param artifactUrl the artifact url
415      * @param distributionId the distribution id
416      * @param status the status
417      * @param errorReason the error reason
418      */
419     private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
420             final String distributionId, final DistributionStatusEnum status, final String errorReason) {
421
422         IDistributionClientResult clientResult;
423         final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
424                 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
425                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
426         final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
427         if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
428             if (errorReason != null) {
429                 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
430             } else {
431                 clientResult = distributionClient.sendDownloadStatus(message);
432             }
433         } else {
434             if (errorReason != null) {
435                 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
436             } else {
437                 clientResult = distributionClient.sendDeploymentStatus(message);
438             }
439         }
440         final StringBuilder loggerMessage = new StringBuilder();
441         loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
442                 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
443                 .append(statusType.name()).append(" Status: ").append(status.name());
444         if (errorReason != null) {
445             loggerMessage.append(" ErrorReason: ").append(errorReason);
446         }
447         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
448             loggerMessage.insert(0, "Failed sending ");
449             LOGGER.debug(loggerMessage);
450         } else {
451             loggerMessage.insert(0, "Successfully Sent ");
452             LOGGER.debug(loggerMessage);
453         }
454     }
455
456     /**
457      * Sends the component done status to SDC using the input values.
458      *
459      * @param distributionId the distribution Id
460      * @param status the distribution status
461      * @param errorReason the error reason
462      */
463     private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
464             final String errorReason) {
465         IDistributionClientResult clientResult;
466         final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
467                 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
468                 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
469         final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
470         if (errorReason == null) {
471             clientResult = distributionClient.sendComponentDoneStatus(message);
472         } else {
473             clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
474         }
475
476         final StringBuilder loggerMessage = new StringBuilder();
477         loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
478                 .append(distributionId).append(" Status: ").append(status.name());
479         if (errorReason != null) {
480             loggerMessage.append(" ErrorReason: ").append(errorReason);
481         }
482         if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
483             loggerMessage.insert(0, "Failed sending ");
484             LOGGER.debug(loggerMessage);
485         } else {
486             loggerMessage.insert(0, "Successfully Sent ");
487             LOGGER.debug(loggerMessage);
488         }
489     }
490
491     /**
492      * Handle the status change of {@link SdcReceptionHandler} to Idle.
493      *
494      * @param newStatus the new status
495      */
496     private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
497         if (nbOfNotificationsOngoing > 1) {
498             --nbOfNotificationsOngoing;
499         } else {
500             nbOfNotificationsOngoing = 0;
501             sdcReceptionHandlerStatus = newStatus;
502         }
503     }
504 }