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