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