2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2018 Ericsson. All rights reserved.
4 * Copyright (C) 2019, 2022 Nordix Foundation.
5 * Modifications Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * SPDX-License-Identifier: Apache-2.0
21 * ============LICENSE_END=========================================================
24 package org.onap.policy.distribution.reception.handling.sdc;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.util.Optional;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import org.onap.policy.common.parameters.ParameterService;
34 import org.onap.policy.distribution.model.Csar;
35 import org.onap.policy.distribution.reception.decoding.PolicyDecodingException;
36 import org.onap.policy.distribution.reception.handling.AbstractReceptionHandler;
37 import org.onap.policy.distribution.reception.handling.sdc.SdcClientHandler.SdcClientOperationType;
38 import org.onap.policy.distribution.reception.handling.sdc.exceptions.ArtifactDownloadException;
39 import org.onap.policy.distribution.reception.statistics.DistributionStatisticsManager;
40 import org.onap.sdc.api.IDistributionClient;
41 import org.onap.sdc.api.consumer.IComponentDoneStatusMessage;
42 import org.onap.sdc.api.consumer.IDistributionStatusMessage;
43 import org.onap.sdc.api.consumer.INotificationCallback;
44 import org.onap.sdc.api.notification.IArtifactInfo;
45 import org.onap.sdc.api.notification.INotificationData;
46 import org.onap.sdc.api.results.IDistributionClientDownloadResult;
47 import org.onap.sdc.api.results.IDistributionClientResult;
48 import org.onap.sdc.impl.DistributionClientFactory;
49 import org.onap.sdc.impl.DistributionClientImpl;
50 import org.onap.sdc.utils.DistributionActionResultEnum;
51 import org.onap.sdc.utils.DistributionStatusEnum;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 * Handles reception of inputs from ONAP Service Design and Creation (SDC) from which policies may be decoded.
58 public class SdcReceptionHandler extends AbstractReceptionHandler implements INotificationCallback {
60 private static final Logger LOGGER = LoggerFactory.getLogger(SdcReceptionHandler.class);
61 private static final String SECONDS = "Seconds";
63 private SdcReceptionHandlerStatus sdcReceptionHandlerStatus = SdcReceptionHandlerStatus.STOPPED;
64 private IDistributionClient distributionClient;
65 private SdcConfiguration sdcConfig;
66 private final AtomicInteger nbOfNotificationsOngoing = new AtomicInteger();
67 private int retryDelay;
68 private SdcClientHandler sdcClientHandler;
70 private enum DistributionStatusType {
75 protected void initializeReception(final String parameterGroupName) {
76 final SdcReceptionHandlerConfigurationParameterGroup handlerParameters =
77 ParameterService.get(parameterGroupName);
78 retryDelay = handlerParameters.getRetryDelay() < 30 ? 30 : handlerParameters.getRetryDelay();
79 sdcConfig = new SdcConfiguration(handlerParameters);
80 distributionClient = createSdcDistributionClient();
81 sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.START, retryDelay);
85 public void destroy() {
86 if (distributionClient != null) {
87 sdcClientHandler = new SdcClientHandler(this, SdcClientOperationType.STOP, retryDelay);
92 public void activateCallback(final INotificationData notificationData) {
93 LOGGER.debug("Receieved the notification from SDC with ID: {}", notificationData.getDistributionID());
94 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.BUSY);
95 processCsarServiceArtifacts(notificationData);
96 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
97 LOGGER.debug("Processed the notification from SDC with ID: {}", notificationData.getDistributionID());
101 * Method to change the status of this reception handler instance.
103 * @param newStatus the new status
105 private synchronized void changeSdcReceptionHandlerStatus(final SdcReceptionHandlerStatus newStatus) {
109 sdcReceptionHandlerStatus = newStatus;
112 handleIdleStatusChange(newStatus);
115 nbOfNotificationsOngoing.incrementAndGet();
116 sdcReceptionHandlerStatus = newStatus;
124 * Creates an instance of {@link IDistributionClient} from {@link DistributionClientFactory}.
126 * @return the {@link IDistributionClient} instance
128 protected IDistributionClient createSdcDistributionClient() {
129 return new DistributionClientImpl();
133 * Method to initialize the SDC client.
135 protected void initializeSdcClient() {
137 LOGGER.debug("Initializing the SDC Client...");
138 if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.STOPPED) {
139 LOGGER.error("The SDC Client is already initialized");
142 final IDistributionClientResult clientResult = distributionClient.init(sdcConfig, this);
143 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
144 LOGGER.error("SDC client initialization failed with reason: {}. Initialization will be retried after {} {}",
145 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
148 LOGGER.debug("SDC Client is initialized successfully");
149 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.INIT);
153 * Method to start the SDC client.
155 protected void startSdcClient() {
157 LOGGER.debug("Going to start the SDC Client...");
158 if (sdcReceptionHandlerStatus != SdcReceptionHandlerStatus.INIT) {
159 LOGGER.error("The SDC Client is not initialized");
162 final IDistributionClientResult clientResult = distributionClient.start();
163 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
164 LOGGER.error("SDC client start failed with reason: {}. Start will be retried after {} {}",
165 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
168 LOGGER.debug("SDC Client is started successfully");
169 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.IDLE);
170 sdcClientHandler.cancel();
174 * Method to stop the SDC client.
176 protected void stopSdcClient() {
177 LOGGER.debug("Going to stop the SDC Client...");
178 final IDistributionClientResult clientResult = distributionClient.stop();
179 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
180 LOGGER.error("SDC client stop failed with reason: {}. Stop will be retried after {} {}",
181 clientResult.getDistributionMessageResult(), retryDelay, SECONDS);
184 LOGGER.debug("SDC Client is stopped successfully");
185 changeSdcReceptionHandlerStatus(SdcReceptionHandlerStatus.STOPPED);
186 sdcClientHandler.cancel();
190 * Method to process csar service artifacts from incoming SDC notification.
192 * @param notificationData the notification from SDC
194 public void processCsarServiceArtifacts(final INotificationData notificationData) {
195 var artifactsProcessedSuccessfully = true;
196 DistributionStatisticsManager.updateTotalDistributionCount();
197 for (final IArtifactInfo artifact : notificationData.getServiceArtifacts()) {
199 final IDistributionClientDownloadResult resultArtifact =
200 downloadTheArtifact(artifact, notificationData);
201 final var filePath = writeArtifactToFile(artifact, resultArtifact);
202 final var csarObject = new Csar(filePath.toString());
203 inputReceived(csarObject);
204 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
205 notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_OK, null);
206 deleteArtifactFile(filePath);
207 } catch (final ArtifactDownloadException | PolicyDecodingException exp) {
208 LOGGER.error("Failed to process csar service artifacts ", exp);
209 artifactsProcessedSuccessfully = false;
210 sendDistributionStatus(DistributionStatusType.DEPLOY, artifact.getArtifactURL(),
211 notificationData.getDistributionID(), DistributionStatusEnum.DEPLOY_ERROR,
212 "Failed to deploy the artifact due to: " + exp.getMessage());
215 if (artifactsProcessedSuccessfully) {
216 DistributionStatisticsManager.updateDistributionSuccessCount();
217 sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_OK,
220 DistributionStatisticsManager.updateDistributionFailureCount();
221 sendComponentDoneStatus(notificationData.getDistributionID(), DistributionStatusEnum.COMPONENT_DONE_ERROR,
222 "Failed to process the artifact");
227 * Method to download the distribution artifact.
229 * @param artifact the artifact
230 * @return the download result
231 * @throws ArtifactDownloadException if download fails
233 private IDistributionClientDownloadResult downloadTheArtifact(final IArtifactInfo artifact,
234 final INotificationData notificationData)
235 throws ArtifactDownloadException {
237 DistributionStatisticsManager.updateTotalDownloadCount();
238 final IDistributionClientDownloadResult downloadResult = distributionClient.download(artifact);
239 if (!downloadResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
240 DistributionStatisticsManager.updateDownloadFailureCount();
241 final String message = "Failed to download artifact with name: " + artifact.getArtifactName() + " due to: "
242 + downloadResult.getDistributionMessageResult();
243 LOGGER.error(message);
244 sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
245 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_ERROR, message);
246 throw new ArtifactDownloadException(message);
248 DistributionStatisticsManager.updateDownloadSuccessCount();
249 sendDistributionStatus(DistributionStatusType.DOWNLOAD, artifact.getArtifactURL(),
250 notificationData.getDistributionID(), DistributionStatusEnum.DOWNLOAD_OK, null);
251 return downloadResult;
255 * Method to write the downloaded distribution artifact to local file system.
257 * @param artifact the notification artifact
258 * @param resultArtifact the download result artifact
259 * @return the local path of written file
260 * @throws ArtifactDownloadException if error occurs while writing the artifact
262 private Path writeArtifactToFile(final IArtifactInfo artifact,
263 final IDistributionClientDownloadResult resultArtifact)
264 throws ArtifactDownloadException {
266 final byte[] payloadBytes = resultArtifact.getArtifactPayload();
268 final var tempArtifactFile = Optional.ofNullable(safelyCreateFile(artifact.getArtifactName()))
269 .orElseThrow(() -> new ArtifactDownloadException("Failed to create temporary file."));
270 try (var fileOutputStream = new FileOutputStream(tempArtifactFile)) {
271 fileOutputStream.write(payloadBytes, 0, payloadBytes.length);
272 return tempArtifactFile.toPath();
274 } catch (final Exception exp) {
275 throw new ArtifactDownloadException("Failed to write artifact to local repository", exp);
280 * Method to delete the downloaded notification artifact from local file system.
282 * @param filePath the path of file
284 private void deleteArtifactFile(final Path filePath) {
286 Files.deleteIfExists(filePath);
287 } catch (final IOException exp) {
288 LOGGER.error("Failed to delete the downloaded artifact file", exp);
293 * Sends the distribution status to SDC using the input values.
295 * @param statusType the status type
296 * @param artifactUrl the artifact url
297 * @param distributionId the distribution id
298 * @param status the status
299 * @param errorReason the error reason
301 private void sendDistributionStatus(final DistributionStatusType statusType, final String artifactUrl,
302 final String distributionId, final DistributionStatusEnum status,
303 final String errorReason) {
305 IDistributionClientResult clientResult;
306 final IDistributionStatusMessage message = DistributionStatusMessage.builder().artifactUrl(artifactUrl)
307 .consumerId(sdcConfig.getConsumerID()).distributionId(distributionId).distributionStatus(status)
308 .timestamp(System.currentTimeMillis()).build();
309 if (DistributionStatusType.DOWNLOAD.equals(statusType)) {
310 if (errorReason != null) {
311 clientResult = distributionClient.sendDownloadStatus(message, errorReason);
313 clientResult = distributionClient.sendDownloadStatus(message);
316 if (errorReason != null) {
317 clientResult = distributionClient.sendDeploymentStatus(message, errorReason);
319 clientResult = distributionClient.sendDeploymentStatus(message);
322 final var loggerMessage = new StringBuilder();
323 loggerMessage.append("distribution status to SDC with values - ").append("DistributionId")
324 .append(distributionId).append(" Artifact: ").append(artifactUrl).append(" StatusType: ")
325 .append(statusType.name()).append(" Status: ").append(status.name());
326 if (errorReason != null) {
327 loggerMessage.append(" ErrorReason: ").append(errorReason);
329 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
330 loggerMessage.insert(0, "Failed sending ");
331 LOGGER.debug("Failed sending {}", loggerMessage);
333 loggerMessage.insert(0, "Successfully Sent ");
334 LOGGER.debug("Successfully Sent {}", loggerMessage);
339 * Sends the component done status to SDC using the input values.
341 * @param distributionId the distribution Id
342 * @param status the distribution status
343 * @param errorReason the error reason
345 private void sendComponentDoneStatus(final String distributionId, final DistributionStatusEnum status,
346 final String errorReason) {
347 IDistributionClientResult clientResult;
348 final IComponentDoneStatusMessage message = ComponentDoneStatusMessage.builder()
349 .consumerId(sdcConfig.getConsumerID()).distributionId(distributionId).distributionStatus(status)
350 .timestamp(System.currentTimeMillis()).build();
351 if (errorReason == null) {
352 clientResult = distributionClient.sendComponentDoneStatus(message);
354 clientResult = distributionClient.sendComponentDoneStatus(message, errorReason);
357 final var loggerMessage = new StringBuilder();
358 loggerMessage.append("component done status to SDC with values - ").append("DistributionId")
359 .append(distributionId).append(" Status: ").append(status.name());
360 if (errorReason != null) {
361 loggerMessage.append(" ErrorReason: ").append(errorReason);
363 if (!clientResult.getDistributionActionResult().equals(DistributionActionResultEnum.SUCCESS)) {
364 LOGGER.debug("Failed sending {}", loggerMessage);
366 LOGGER.debug("Successfully Sent {}", loggerMessage);
371 * Handle the status change of {@link SdcReceptionHandler} to Idle.
373 * @param newStatus the new status
375 private void handleIdleStatusChange(final SdcReceptionHandlerStatus newStatus) {
376 if (nbOfNotificationsOngoing.getAndUpdate(curval -> Math.max(0, curval - 1)) == 0) {
377 sdcReceptionHandlerStatus = newStatus;
381 private File safelyCreateFile(String prefix) throws IOException {
382 File file = Files.createTempFile(prefix, ".csar").toFile();
383 if (file.setReadable(true, false)
384 && file.setWritable(true, true)) {