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