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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.distribution.reception.handling.sdc;
24 import java.io.FileOutputStream;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
34 import org.onap.policy.common.logging.flexlogger.FlexLogger;
35 import org.onap.policy.common.logging.flexlogger.Logger;
36 import org.onap.policy.common.parameters.ParameterService;
37 import org.onap.policy.distribution.model.CloudArtifact;
38 import org.onap.policy.distribution.model.Csar;
39 import org.onap.policy.distribution.model.GsonUtil;
40 import org.onap.policy.distribution.model.VfModuleModel;
41 import org.onap.policy.distribution.reception.decoding.PolicyDecodingException;
42 import org.onap.policy.distribution.reception.handling.AbstractReceptionHandler;
43 import org.onap.policy.distribution.reception.handling.sdc.SdcClientHandler.SdcClientOperationType;
44 import org.onap.policy.distribution.reception.handling.sdc.exceptions.ArtifactDownloadException;
45 import org.onap.policy.distribution.reception.statistics.DistributionStatisticsManager;
46 import org.onap.sdc.api.IDistributionClient;
47 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
48 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
49 import org.onap.sdc.api.consumer.INotificationCallback;
50 import org.onap.sdc.api.notification.IArtifactInfo;
51 import org.onap.sdc.api.notification.INotificationData;
52 import org.onap.sdc.api.notification.IResourceInstance;
53 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
54 import org.onap.sdc.api.results.IDistributionClientResult;
55 import org.onap.sdc.impl.DistributionClientFactory;
56 import org.onap.sdc.utils.DistributionActionResultEnum;
57 import org.onap.sdc.utils.DistributionStatusEnum;
60 * Handles reception of inputs from ONAP Service Design and Creation (SDC) from which policies may be decoded.
62 public class SdcReceptionHandler extends AbstractReceptionHandler implements INotificationCallback {
64 private static final Logger LOGGER = FlexLogger.getLogger(SdcReceptionHandler.class);
65 private static final String SECONDS = "Seconds";
67 private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
68 private IDistributionClient distributionClient;
69 private SdcConfiguration sdcConfig;
70 private volatile int nbOfNotificationsOngoing = 0;
71 private int retryDelay;
72 private SdcClientHandler sdcClientHandler;
74 private enum DistributionStatusType {
79 protected void initializeReception(final String parameterGroupName) {
80 final SdcReceptionHandlerConfigurationParameterGroup handlerParameters =
81 ParameterService.get(parameterGroupName);
82 retryDelay = handlerParameters.getRetryDelay() < 30 ? 30 : handlerParameters.getRetryDelay();
83 sdcConfig = new SdcConfiguration(handlerParameters);
84 distributionClient = createSdcDistributionClient();
85 sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.START, retryDelay);
89 public void destroy() {
90 if (distributionClient != null) {
91 sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.STOP, retryDelay);
96 public void activateCallback(final INotificationData notificationData) {
97 LOGGER.debug("Receieved the notification from SDC with ID: " + notificationData.getDistributionID());
98 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.BUSY);
99 //Process only the resource artifacts in MC
100 for (IResourceInstance resource : notificationData.getResources()) {
101 // We process only VNF resource in MC
102 if ("VF".equals(resource.getResourceType())) {
103 this.processVfModulesArtifacts(notificationData,resource);
106 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
107 LOGGER.debug("Processed the notification from SDC with ID: " + notificationData.getDistributionID());
111 * Method to change the status of this reception handler instance.
113 * @param newStatus the new status
115 private final synchronized void changeSdcReceptionHandlerStatus(final SdcReceptionHandlerStatus newStatus) {
119 sdcReceptionHandlerStatus = newStatus;
122 handleIdleStatusChange(newStatus);
125 ++nbOfNotificationsOngoing;
126 sdcReceptionHandlerStatus = newStatus;
134 * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
136 * @return the {@link IDistributionClient} instance
138 protected IDistributionClient createSdcDistributionClient() {
139 return DistributionClientFactory.createDistributionClient();
143 * Method to initialize the SDC client.
146 protected void initializeSdcClient() {
148 LOGGER.debug("Initializing the SDC Client...");
149 if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
150 LOGGER.error("The SDC Client is already initialized");
153 final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
154 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
155 LOGGER.error("SDC client initialization failed with reason:" + clientResult.getDistributionMessageResult()
156 + ". Initialization will be retried after " + retryDelay + SECONDS);
159 LOGGER.debug("SDC Client is initialized successfully");
160 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
164 * Method to start the SDC client.
167 protected void startSdcClient() {
169 LOGGER.debug("Going to start the SDC Client...");
170 if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.INIT) {
171 LOGGER.error("The SDC Client is not initialized");
174 final IDistributionClientResult clientResult = distributionClient.start();
175 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
176 LOGGER.error("SDC client start failed with reason:" + clientResult.getDistributionMessageResult()
177 + ". Start will be retried after " + retryDelay + SECONDS);
180 LOGGER.debug("SDC Client is started successfully");
181 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
182 sdcClientHandler.cancel();
186 * Method to stop the SDC client.
189 protected void stopSdcClient() {
190 LOGGER.debug("Going to stop the SDC Client...");
191 final IDistributionClientResult clientResult = distributionClient.stop();
192 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
193 LOGGER.error("SDC client stop failed with reason:" + clientResult.getDistributionMessageResult()
194 + ". Stop will be retried after " + retryDelay + SECONDS);
197 LOGGER.debug("SDC Client is stopped successfully");
198 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
199 sdcClientHandler.cancel();
203 * Method to process VF MODULES_METADATA artifacts from incoming SDC notification.
205 * @param artifact the item needs to check if it has been stored in DBB or not
207 private boolean checkArtifactAlreadyStored(IArtifactInfo artifact) {
214 * Method to process VF MODULES_METADATA artifacts from incoming SDC notification.
216 * @param notificationData the notification from SDC
217 * @param resource every resource of the service from SDC
219 public void processVfModulesArtifacts(final INotificationData notificationData, IResourceInstance resource) {
220 boolean artifactsProcessedSuccessfully = true;
221 DistributionStatisticsManager.updateTotalDistributionCount();
222 List<String> relevantArtifactTypes = sdcConfig.getRelevantArtifactTypes();
223 Path path = Paths.get("/data");
224 ArrayList<VfModuleModel> vfModuleModels = new ArrayList<>();
225 HashMap<String, IArtifactInfo> artifactMap = new HashMap<>();//key is UUID, value is artifact for shared folder
226 String vfArtifactData = null;
228 for (final IArtifactInfo artifact : resource.getArtifacts()) {
229 artifactMap.put(artifact.getArtifactUUID(),artifact);
231 //extract the artifactlist and write them into MongoDB
232 if (artifact.getArtifactType().equals("VF_MODULES_METADATA")) {
234 final IDistributionClientDownloadResult resultArtifact =
235 downloadTheArtifact(artifact,notificationData);
236 vfArtifactData = new String(resultArtifact.getArtifactPayload());
237 vfModuleModels = GsonUtil.parseJsonArrayWithGson(vfArtifactData,VfModuleModel.class);
238 } catch (final ArtifactDownloadException exp) {
239 LOGGER.error("Failed to process csar service artifacts ", exp);
240 artifactsProcessedSuccessfully = false;
241 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
242 notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
243 "Failed to deploy the artifact due to: " + exp.getMessage());
248 //foreach(vf_module_metadata)
249 // 1. create a dir like /data/UUID1
250 // 2. put the vfmodule-meta.json into it
251 // 3. put the service-meta.json into it
252 // 3. go through each aritfact uuid under artifact_list of vf_module and download
253 for (final VfModuleModel vfModule : vfModuleModels) {
256 Path temp = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID());
257 path = Files.createDirectory(temp);//create UUID path
258 //store the value to vfmodule-meta.json
259 String filePath = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID(),
260 "vfmodule-meta.json").toString();
261 writeFileByFileWriter(filePath, vfArtifactData);
262 //store the service level info to serivce-meta.json
263 filePath = Paths.get("/data",vfModule.getVfModuleModelCustomizationUUID(),
264 "service-meta.json").toString();
265 writeFileByFileWriter(filePath, notificationData.toString());
266 } catch (final IOException exp) {
267 LOGGER.error("Failed to create directory artifact file", exp);
270 for(final String uuid : vfModule.getArtifacts()) {
272 IArtifactInfo artifact = artifactMap.get(uuid);
273 final IDistributionClientDownloadResult resultArtifact = downloadTheArtifact(artifact, notificationData);
274 writeArtifactToDir(artifact,resultArtifact,path);
275 } catch (final ArtifactDownloadException exp) {
276 LOGGER.error("Failed to process csar service artifacts ", exp);
277 artifactsProcessedSuccessfully = false;
278 sendDistributionStatus(DistributionStatusType.DEPLOY, artifactMap.get(uuid).getArtifactURL(),
279 notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
280 "Failed to deploy the artifact due to: " + exp.getMessage());
287 final CloudArtifact cloudArtifact = new CloudArtifact(vfModuleModels, artifactMap);
288 inputReceived(cloudArtifact);
289 } catch ( final PolicyDecodingException exp) {
290 LOGGER.error("Failed to process cloud artifacts ", exp);
291 artifactsProcessedSuccessfully = false;
294 if (artifactsProcessedSuccessfully) {
295 DistributionStatisticsManager.updateDistributionSuccessCount();
296 sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
299 DistributionStatisticsManager.updateDistributionFailureCount();
300 sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
301 "Failed to process the artifact");
306 * Method to download the distribution artifact.
308 * @param artifact the artifact
309 * @return the download result
310 * @throws ArtifactDownloadException if download fails
312 private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
313 final INotificationData notificationData) throws ArtifactDownloadException {
315 DistributionStatisticsManager.updateTotalDownloadCount();
316 final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
317 if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
318 DistributionStatisticsManager.updateDownloadFailureCount();
319 final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
320 + downloadResult.getDistributionMessageResult();
321 LOGGER.error(message);
322 sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
323 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
324 throw new ArtifactDownloadException(message);
326 DistributionStatisticsManager.updateDownloadSuccessCount();
327 sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
328 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
329 return downloadResult;
333 * Method to write a string to a file
334 * @Param filePath the file's path
335 * @Param content the data to be writen
336 * @throws IOException if error happends
338 private static void writeFileByFileWriter(String filePath, String content) throws IOException {
339 File file = new File(filePath);
340 synchronized (file) {
342 FileWriter fw = new FileWriter(filePath);
345 } catch (final IOException exp) {
346 LOGGER.error("Failed to write File by File Writer ", exp);
351 * Method to write the downloaded distribution artifact to local file system with specified dir.
353 * @param artifact the notification artifact
354 * @param resultArtifact the download result artifact
355 * @return the local path of written file
356 * @throws ArtifactDownloadException if error occurs while writing the artifact
358 private Path writeArtifactToDir(final IArtifactInfo artifact,
359 final IDistributionClientDownloadResult resultArtifact, Path path) throws ArtifactDownloadException {
361 final byte[] payloadBytes = resultArtifact.getArtifactPayload();
362 //final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
363 File tempArtifactFile = new File(path.toString() + "/" + artifact.getArtifactName());
364 try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
365 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
366 return tempArtifactFile.toPath();
368 } catch (final Exception exp) {
369 final String message = "Failed to write artifact to local repository";
370 LOGGER.error(message, exp);
371 throw new ArtifactDownloadException(message, exp);
376 * Method to write the downloaded distribution artifact to local file system.
378 * @param artifact the notification artifact
379 * @param resultArtifact the download result artifact
380 * @return the local path of written file
381 * @throws ArtifactDownloadException if error occurs while writing the artifact
383 private Path writeArtifactToFile(final IArtifactInfo artifact,
384 final IDistributionClientDownloadResult resultArtifact) throws ArtifactDownloadException {
386 final byte[] payloadBytes = resultArtifact.getArtifactPayload();
387 final File tempArtifactFile = File.createTempFile(artifact.getArtifactName(), ".csar");
388 try (FileOutputStream fileOutputStream = new FileOutputStream(tempArtifactFile)) {
389 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
390 return tempArtifactFile.toPath();
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);
400 * Method to delete the downloaded notification artifact from local file system.
402 * @param filePath the path of file
404 private void deleteArtifactFile(final Path filePath) {
406 Files.deleteIfExists(filePath);
407 } catch (final IOException exp) {
408 LOGGER.error("Failed to delete the downloaded artifact file", exp);
413 * Sends the distribution status to SDC using the input values.
415 * @param statusType the status type
416 * @param artifactUrl the artifact url
417 * @param distributionId the distribution id
418 * @param status the status
419 * @param errorReason the error reason
421 private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
422 final String distributionId, final DistributionStatusEnum status, final String errorReason) {
424 IDistributionClientResult clientResult;
425 final DistributionStatusMessageBuilder messageBuilder = new DistributionStatusMessageBuilder()
426 .setArtifactUrl(artifactUrl).setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
427 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
428 final IDistributionStatusMessage message = new DistributionStatusMessage(messageBuilder);
429 if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
430 if (errorReason != null) {
431 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
433 clientResult = distributionClient.sendDownloadStatus(message);
436 if (errorReason != null) {
437 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
439 clientResult = distributionClient.sendDeploymentStatus(message);
442 final StringBuilder loggerMessage = new StringBuilder();
443 loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
444 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
445 .append(statusType.name()).append(" Status: ").append(status.name());
446 if (errorReason != null) {
447 loggerMessage.append(" ErrorReason: ").append(errorReason);
449 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
450 loggerMessage.insert(0, "Failed sending ");
451 LOGGER.debug(loggerMessage);
453 loggerMessage.insert(0, "Successfully Sent ");
454 LOGGER.debug(loggerMessage);
459 * Sends the component done status to SDC using the input values.
461 * @param distributionId the distribution Id
462 * @param status the distribution status
463 * @param errorReason the error reason
465 private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
466 final String errorReason) {
467 IDistributionClientResult clientResult;
468 final ComponentDoneStatusMessageBuilder messageBuilder = new ComponentDoneStatusMessageBuilder()
469 .setConsumerId(sdcConfig.getConsumerID()).setDistributionId(distributionId)
470 .setDistributionStatus(status).setTimestamp(System.currentTimeMillis());
471 final IComponentDoneStatusMessage message = new ComponentDoneStatusMessage(messageBuilder);
472 if (errorReason == null) {
473 clientResult = distributionClient.sendComponentDoneStatus(message);
475 clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
478 final StringBuilder loggerMessage = new StringBuilder();
479 loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
480 .append(distributionId).append(" Status: ").append(status.name());
481 if (errorReason != null) {
482 loggerMessage.append(" ErrorReason: ").append(errorReason);
484 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
485 loggerMessage.insert(0, "Failed sending ");
486 LOGGER.debug(loggerMessage);
488 loggerMessage.insert(0, "Successfully Sent ");
489 LOGGER.debug(loggerMessage);
494 * Handle the status change of {@link SdcReceptionHandler} to Idle.
496 * @param newStatus the new status
498 private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
499 if (nbOfNotificationsOngoing > 1) {
500 --nbOfNotificationsOngoing;
502 nbOfNotificationsOngoing = 0;
503 sdcReceptionHandlerStatus = newStatus;