/**
* ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
* Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
* Copyright © 2017-2018 European Software Marketing Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============LICENSE_END=========================================================
*/
package org.onap.aai.modelloader.notification;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.onap.aai.babel.service.data.BabelArtifact;
import org.onap.aai.babel.service.data.BabelArtifact.ArtifactType;
import org.onap.aai.cl.api.Logger;
import org.onap.aai.cl.eelf.LoggerFactory;
import org.onap.aai.cl.mdc.MdcContext;
import org.onap.aai.cl.mdc.MdcOverride;
import org.onap.aai.modelloader.config.ModelLoaderConfig;
import org.onap.aai.modelloader.entity.Artifact;
import org.onap.aai.modelloader.entity.model.BabelArtifactParsingException;
import org.onap.aai.modelloader.entity.model.IModelParser;
import org.onap.aai.modelloader.entity.model.NamedQueryArtifactParser;
import org.onap.aai.modelloader.extraction.InvalidArchiveException;
import org.onap.aai.modelloader.restclient.BabelServiceClient;
import org.onap.aai.modelloader.restclient.BabelServiceClientFactory;
import org.onap.aai.modelloader.service.ModelLoaderMsgs;
import org.onap.sdc.api.IDistributionClient;
import org.onap.sdc.api.notification.IArtifactInfo;
import org.onap.sdc.api.notification.INotificationData;
import org.onap.sdc.api.results.IDistributionClientDownloadResult;
import org.onap.sdc.utils.ArtifactTypeEnum;
import org.onap.sdc.utils.DistributionActionResultEnum;
/**
* This class is responsible for downloading the artifacts from the ASDC.
*
* The downloads can be TOSCA_CSAR files or VNF_CATALOG files.
*
* The status of the download is published. The status of the extraction of yml files from a TOSCA_CSAR file is also
* published as a deployment event.
*
* TOSCA_CSAR file artifacts will be converted into XML and returned as model artifacts.
*/
public class ArtifactDownloadManager {
private static Logger logger = LoggerFactory.getInstance().getLogger(ArtifactDownloadManager.class);
private IDistributionClient client;
private NotificationPublisher notificationPublisher;
private BabelArtifactConverter babelArtifactConverter;
private ModelLoaderConfig config;
private BabelServiceClientFactory clientFactory;
public ArtifactDownloadManager(IDistributionClient client, ModelLoaderConfig config,
BabelServiceClientFactory clientFactory) {
this.client = client;
this.config = config;
this.clientFactory = clientFactory;
}
/**
* This method downloads the artifacts from the ASDC.
*
* @param data data about the notification that is being processed
* @param artifacts the specific artifacts found in the data.
* @param modelArtifacts collection of artifacts for model query specs
* @param catalogArtifacts collection of artifacts that represent vnf catalog files
* @return boolean true
if the download process was successful otherwise false
*/
boolean downloadArtifacts(INotificationData data, List artifacts, List modelArtifacts,
List catalogArtifacts) {
boolean success = true;
for (IArtifactInfo artifact : artifacts) {
try {
IDistributionClientDownloadResult downloadResult = downloadIndividualArtifacts(data, artifact);
processDownloadedArtifacts(modelArtifacts, catalogArtifacts, artifact, downloadResult, data);
} catch (DownloadFailureException e) {
getNotificationPublisher().publishDownloadFailure(client, data, artifact, e.getMessage());
success = false;
} catch (Exception e) {
getNotificationPublisher().publishDeployFailure(client, data, artifact);
success = false;
}
if (!success) {
break;
}
}
return success;
}
private IDistributionClientDownloadResult downloadIndividualArtifacts(INotificationData data,
IArtifactInfo artifact) throws DownloadFailureException {
// Grab the current time so we can measure the download time for the metrics log
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
MdcOverride override = new MdcOverride();
override.addAttribute(MdcContext.MDC_START_TIME, ZonedDateTime.now().format(formatter));
IDistributionClientDownloadResult downloadResult = client.download(artifact);
logger.info(ModelLoaderMsgs.DOWNLOAD_COMPLETE, downloadResult.getDistributionActionResult().toString(),
downloadResult.getArtifactPayload() == null ? "null"
: Base64.getEncoder().encodeToString(downloadResult.getArtifactPayload()));
if (DistributionActionResultEnum.SUCCESS.equals(downloadResult.getDistributionActionResult())) {
logger.info(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Downloaded artifact: " + artifact.getArtifactName());
getNotificationPublisher().publishDownloadSuccess(client, data, artifact);
} else {
throw new DownloadFailureException(downloadResult.getDistributionMessageResult());
}
return downloadResult;
}
private void processDownloadedArtifacts(List modelArtifacts, List catalogArtifacts,
IArtifactInfo artifactInfo, IDistributionClientDownloadResult downloadResult, INotificationData data)
throws ProcessToscaArtifactsException, InvalidArchiveException, BabelArtifactParsingException {
if ("TOSCA_CSAR".equalsIgnoreCase(artifactInfo.getArtifactType())) {
processToscaArtifacts(modelArtifacts, catalogArtifacts, downloadResult.getArtifactPayload(), artifactInfo,
data.getDistributionID(), data.getServiceVersion());
} else if (ArtifactTypeEnum.MODEL_QUERY_SPEC.toString().equalsIgnoreCase(artifactInfo.getArtifactType())) {
processModelQuerySpecArtifact(modelArtifacts, downloadResult);
} else {
logger.info(ModelLoaderMsgs.UNSUPPORTED_ARTIFACT_TYPE, artifactInfo.getArtifactName(),
artifactInfo.getArtifactType());
throw new InvalidArchiveException("Unsupported artifact type: " + artifactInfo.getArtifactType());
}
}
public void processToscaArtifacts(List modelArtifacts, List catalogArtifacts, byte[] payload,
IArtifactInfo artifactInfo, String distributionId, String serviceVersion)
throws ProcessToscaArtifactsException {
try {
BabelServiceClient babelClient = createBabelServiceClient(artifactInfo, serviceVersion);
logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT,
"Posting artifact: " + artifactInfo.getArtifactName() + ", version: " + serviceVersion);
List babelArtifacts =
babelClient.postArtifact(payload, artifactInfo.getArtifactName(), serviceVersion, distributionId);
// Sort Babel artifacts based on type
Map> artifactMap =
babelArtifacts.stream().collect(Collectors.groupingBy(BabelArtifact::getType));
if (artifactMap.containsKey(BabelArtifact.ArtifactType.MODEL)) {
modelArtifacts.addAll(
getBabelArtifactConverter().convertToModel(artifactMap.get(BabelArtifact.ArtifactType.MODEL)));
artifactMap.remove(BabelArtifact.ArtifactType.MODEL);
}
if (artifactMap.containsKey(BabelArtifact.ArtifactType.VNFCATALOG)) {
catalogArtifacts.addAll(getBabelArtifactConverter()
.convertToCatalog(artifactMap.get(BabelArtifact.ArtifactType.VNFCATALOG)));
artifactMap.remove(BabelArtifact.ArtifactType.VNFCATALOG);
}
// Log unexpected artifact types
if (!artifactMap.isEmpty()) {
logger.warn(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
artifactInfo.getArtifactName() + " " + serviceVersion
+ ". Unexpected artifact types returned by the babel service: "
+ artifactMap.keySet().toString());
}
} catch (BabelArtifactParsingException e) {
logger.error(ModelLoaderMsgs.ARTIFACT_PARSE_ERROR,
"Error for artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + e);
throw new ProcessToscaArtifactsException(
"An error occurred while trying to parse the Babel artifacts: " + e.getLocalizedMessage());
} catch (Exception e) {
logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
"Error posting artifact " + artifactInfo.getArtifactName() + " " + serviceVersion + " to Babel: "
+ e.getLocalizedMessage());
throw new ProcessToscaArtifactsException(
"An error occurred while calling the Babel service: " + e.getLocalizedMessage());
}
}
BabelServiceClient createBabelServiceClient(IArtifactInfo artifact, String serviceVersion)
throws ProcessToscaArtifactsException {
BabelServiceClient babelClient;
try {
logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Creating Babel client");
babelClient = clientFactory.create(config);
} catch (Exception e) {
logger.error(ModelLoaderMsgs.BABEL_REST_REQUEST_ERROR, e, "POST", config.getBabelBaseUrl(),
"Error posting artifact " + artifact.getArtifactName() + " " + serviceVersion + " to Babel: "
+ e.getLocalizedMessage());
throw new ProcessToscaArtifactsException(
"An error occurred tyring to convert the tosca artifacts to xml artifacts: "
+ e.getLocalizedMessage());
}
return babelClient;
}
private void processModelQuerySpecArtifact(List modelArtifacts,
IDistributionClientDownloadResult downloadResult) throws BabelArtifactParsingException {
logger.debug(ModelLoaderMsgs.DISTRIBUTION_EVENT, "Processing named query artifact.");
IModelParser parser = new NamedQueryArtifactParser();
List parsedArtifacts =
parser.parse(new String(downloadResult.getArtifactPayload()), downloadResult.getArtifactFilename());
if (parsedArtifactsExist(parsedArtifacts)) {
modelArtifacts.addAll(parsedArtifacts);
} else {
throw new BabelArtifactParsingException(
"Could not parse generated XML: " + new String(downloadResult.getArtifactPayload()));
}
}
private boolean parsedArtifactsExist(List parsedArtifacts) {
return parsedArtifacts != null && !parsedArtifacts.isEmpty();
}
private NotificationPublisher getNotificationPublisher() {
if (notificationPublisher == null) {
notificationPublisher = new NotificationPublisher();
}
return notificationPublisher;
}
private BabelArtifactConverter getBabelArtifactConverter() {
if (babelArtifactConverter == null) {
babelArtifactConverter = new BabelArtifactConverter();
}
return babelArtifactConverter;
}
}