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