2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights
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.
19 * ============LICENSE_END=========================================================
22 package org.openecomp.sdnc.uebclient;
24 import java.io.BufferedReader;
25 import java.io.ByteArrayInputStream;
26 import java.io.DataOutputStream;
28 import java.io.FileReader;
29 import java.io.FileWriter;
30 import java.io.IOException;
31 import java.io.InputStreamReader;
32 import java.net.Authenticator;
33 import java.net.HttpURLConnection;
34 import java.net.PasswordAuthentication;
36 import java.nio.file.DirectoryStream;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.StandardCopyOption;
40 import java.text.SimpleDateFormat;
41 import java.util.Date;
42 import java.util.LinkedList;
43 import java.util.List;
45 import javax.net.ssl.HostnameVerifier;
46 import javax.net.ssl.HttpsURLConnection;
47 import javax.net.ssl.SSLSession;
48 import javax.xml.parsers.DocumentBuilder;
49 import javax.xml.parsers.DocumentBuilderFactory;
50 import javax.xml.transform.Source;
51 import javax.xml.transform.Transformer;
52 import javax.xml.transform.TransformerFactory;
53 import javax.xml.transform.stream.StreamResult;
54 import javax.xml.transform.stream.StreamSource;
55 import javax.xml.xpath.XPath;
56 import javax.xml.xpath.XPathFactory;
58 import org.apache.commons.codec.binary.Base64;
59 import org.openecomp.sdc.api.IDistributionClient;
60 import org.openecomp.sdc.api.consumer.IDistributionStatusMessage;
61 import org.openecomp.sdc.api.consumer.INotificationCallback;
62 import org.openecomp.sdc.api.notification.IArtifactInfo;
63 import org.openecomp.sdc.api.notification.INotificationData;
64 import org.openecomp.sdc.api.notification.IResourceInstance;
65 import org.openecomp.sdc.api.results.IDistributionClientDownloadResult;
66 import org.openecomp.sdc.api.results.IDistributionClientResult;
67 import org.openecomp.sdc.utils.ArtifactTypeEnum;
68 import org.openecomp.sdc.utils.DistributionActionResultEnum;
69 import org.openecomp.sdc.utils.DistributionStatusEnum;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.w3c.dom.Document;
73 import org.w3c.dom.Element;
75 public class SdncUebCallback implements INotificationCallback {
77 private static final Logger LOG = LoggerFactory
78 .getLogger(SdncUebCallback.class);
80 private static final int NUM_PASSES = 2;
82 private enum SdncArtifactType {
83 VF_LICENSE_MODEL("vf-license-model", "vf-license-model-update",0),
88 private String rpcName;
91 public int getPass() {
95 private SdncArtifactType(String tag, String rpcName, int pass) {
97 this.rpcName = rpcName;
101 public String getTag() {
105 public String getRpcUrl(String base) {
109 public static SdncArtifactType fromTag(String tag) {
111 for (SdncArtifactType artifact: SdncArtifactType.values()) {
112 if (artifact.getTag().equalsIgnoreCase(tag)) {
122 private class SdncAuthenticator extends Authenticator {
124 private final String user;
125 private final String passwd;
127 SdncAuthenticator(String user, String passwd) {
129 this.passwd = passwd;
132 protected PasswordAuthentication getPasswordAuthentication() {
133 return new PasswordAuthentication(user, passwd.toCharArray());
138 private class DeployableArtifact {
139 SdncArtifactType type;
140 IArtifactInfo artifactInfo;
144 String artifactVersion;
147 public String getArtifactName() {
153 public String getArtifactVersion() {
154 return artifactVersion;
158 public SdncArtifactType getType() {
164 public IArtifactInfo getArtifactInfo() {
169 public File getFile() {
176 public DeployableArtifact(SdncArtifactType type, String svcName, String resourceName, IArtifactInfo artifactInfo, File file) {
178 this.artifactInfo = artifactInfo;
179 this.artifactName = artifactInfo.getArtifactName();
180 this.artifactVersion = artifactInfo.getArtifactVersion();
185 public DeployableArtifact(SdncArtifactType type, String svcName, String resourceName, String artifactName, String artifactVersion, File file) {
187 this.artifactInfo = null;
188 this.artifactName = artifactName;
189 this.artifactVersion = artifactVersion;
195 public String getSvcName() {
201 public String getResourceName() {
207 private final IDistributionClient client;
208 private final SdncUebConfiguration config;
210 private final LinkedList<DeployableArtifact> deployList[];
213 public SdncUebCallback(IDistributionClient client, SdncUebConfiguration config) {
214 this.client = client;
215 this.config = config;
216 this.deployList = new LinkedList[NUM_PASSES];
218 for (int i = 0 ; i < NUM_PASSES ; i++) {
219 this.deployList[i] = new LinkedList<>();
224 public void activateCallback(INotificationData data) {
226 LOG.info("Received notification : ("+data.getDistributionID()+","+data.getServiceName()+","+data.getServiceVersion()+
227 ","+data.getServiceDescription());
229 String incomingDirName = config.getIncomingDir();
230 String archiveDirName = config.getArchiveDir();
232 File incomingDir = null;
233 File archiveDir = null;
235 if (!incomingDir.exists()) {
236 incomingDir.mkdirs();
240 if (!archiveDir.exists()) {
244 // Process service level artifacts
245 List<IArtifactInfo> artifactList = data.getServiceArtifacts();
247 if (artifactList != null) {
249 incomingDir = new File(incomingDirName + "/" + escapeFilename(data.getServiceName()));
250 if (!incomingDir.exists()) {
251 incomingDir.mkdirs();
254 archiveDir = new File(archiveDirName + "/" + escapeFilename(data.getServiceName()));
255 if (!archiveDir.exists()) {
258 for (IArtifactInfo curArtifact : artifactList)
261 LOG.info("Received artifact " + curArtifact.getArtifactName());
263 handleArtifact(data, data.getServiceName(), null, curArtifact, incomingDir, archiveDir);
268 // Process resource level artifacts
269 for (IResourceInstance curResource : data.getResources()) {
271 LOG.info("Received resource : "+curResource.getResourceName());
272 artifactList = curResource.getArtifacts();
274 if (artifactList != null) {
276 incomingDir = new File(incomingDirName + "/" + escapeFilename(data.getServiceName()) + "/" + escapeFilename(curResource.getResourceName()));
277 if (!incomingDir.exists()) {
278 incomingDir.mkdirs();
281 archiveDir = new File(archiveDirName + "/" + escapeFilename(data.getServiceName()) + "/" + escapeFilename(curResource.getResourceName()));
282 if (!archiveDir.exists()) {
285 for (IArtifactInfo curArtifact : artifactList)
288 LOG.info("Received artifact " + curArtifact.getArtifactName());
290 handleArtifact(data, data.getServiceName(), curResource.getResourceName(), curArtifact, incomingDir, archiveDir);
295 deployDownloadedFiles(incomingDir, archiveDir, data);
301 public void deployDownloadedFiles(File incomingDir, File archiveDir, INotificationData data) {
303 if (incomingDir == null) {
304 incomingDir = new File(config.getIncomingDir());
306 if (!incomingDir.exists()) {
307 incomingDir.mkdirs();
312 if (archiveDir == null) {
313 archiveDir = new File(config.getArchiveDir());
315 if (!archiveDir.exists()) {
320 String curFileName = "";
321 try (DirectoryStream<Path> stream = Files.newDirectoryStream(incomingDir.toPath())) {
322 for (Path file: stream) {
323 curFileName = file.toString();
324 handleSuccessfulDownload(null,null, null, null, file.toFile(), archiveDir);
326 } catch (Exception x) {
327 // IOException can never be thrown by the iteration.
328 // In this snippet, it can only be thrown by newDirectoryStream.
329 LOG.warn("Cannot process spool file "+ curFileName, x);
332 // Deploy scheduled deployments
334 for (int pass = 0 ; pass < NUM_PASSES ; pass++) {
336 if (deployList[pass] != null) {
337 while (! deployList[pass].isEmpty()) {
338 DeployableArtifact artifact = deployList[pass].pop();
340 DistributionStatusEnum deployResult = DistributionStatusEnum.DEPLOY_ERROR;
345 deployResult = deploySpoolFile(artifact);
346 } catch (Exception e) {
347 LOG.error("Caught exception trying to deploy file", e);
351 IArtifactInfo artifactInfo = artifact.getArtifactInfo();
353 if (artifactInfo != null && data != null) {
354 IDistributionClientResult deploymentStatus;
355 deploymentStatus = client.sendDeploymentStatus(buildStatusMessage(
356 client, data, artifactInfo,
365 private void handleArtifact(INotificationData data, String svcName, String resourceName, IArtifactInfo artifact, File incomingDir, File archiveDir) {
368 IDistributionClientDownloadResult downloadResult = client
371 String payload = new String(downloadResult.getArtifactPayload());
374 File spoolFile = new File(incomingDir.getAbsolutePath() + "/" + artifact.getArtifactName());
376 boolean writeSucceeded = false;
379 FileWriter spoolFileWriter = new FileWriter(spoolFile);
380 spoolFileWriter.write(payload);
381 spoolFileWriter.close();
382 writeSucceeded = true;
383 } catch (Exception e) {
384 LOG.error("Unable to save downloaded file to spool directory ("+ incomingDir.getAbsolutePath() +")", e);
388 if (writeSucceeded && downloadResult.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS) {
389 handleSuccessfulDownload(data, svcName, resourceName, artifact, spoolFile, archiveDir);
393 handleFailedDownload(data, artifact);
398 private void handleFailedDownload(INotificationData data,
399 IArtifactInfo relevantArtifact) {
400 // Send Download Status
401 IDistributionClientResult sendDownloadStatus = client
402 .sendDownloadStatus(buildStatusMessage(client, data,
403 relevantArtifact, DistributionStatusEnum.DOWNLOAD_ERROR));
406 private void handleSuccessfulDownload(INotificationData data, String svcName, String resourceName,
407 IArtifactInfo artifact, File spoolFile, File archiveDir) {
409 if (data != null && artifact != null) {
410 // Send Download Status
411 IDistributionClientResult sendDownloadStatus = client
412 .sendDownloadStatus(buildStatusMessage(client, data, artifact, DistributionStatusEnum.DOWNLOAD_OK));
415 // If an override file exists, read that instead of the file we just downloaded
416 ArtifactTypeEnum artifactEnum = ArtifactTypeEnum.YANG_XML;
418 if (artifact != null) {
419 artifact.getArtifactType();
421 String overrideFileName = config.getOverrideFile();
422 if (overrideFileName != null && overrideFileName.length() > 0) {
423 File overrideFile = new File(overrideFileName);
425 if (overrideFile.exists()) {
426 artifactEnum = ArtifactTypeEnum.YANG_XML;
427 spoolFile = overrideFile;
432 // Process spool file
433 Document spoolDoc = null;
434 File transformedFile = null;
436 // Apply XSLTs and get Doc object
438 transformedFile = applyXslts(spoolFile);
439 } catch (Exception e) {
440 LOG.error("Caught exception trying to parse XML file", e);
443 if (transformedFile != null) {
448 DocumentBuilderFactory dbf = DocumentBuilderFactory
450 DocumentBuilder db = dbf.newDocumentBuilder();
452 spoolDoc = db.parse(transformedFile);
453 } catch (Exception e) {
455 "Caught exception trying to parse transformed XML file "
456 + transformedFile.getAbsolutePath(), e);
459 } catch (Exception e) {
460 LOG.error("Caught exception trying to deploy file", e);
465 if (spoolDoc != null) {
467 SdncArtifactType artifactType = analyzeFileType(artifactEnum,
468 spoolFile, spoolDoc);
470 if (artifactType != SdncArtifactType.UNKNOWN) {
472 scheduleDeployment(artifactType, svcName, resourceName, artifact, spoolFile.getName(), transformedFile);
476 // SDNGC-2660 : Move file to archive directory even if it is an unrecognized type so that
477 // we do not keep trying and failing to process it.
479 Path source = spoolFile.toPath();
480 Path targetDir = archiveDir.toPath();
482 Files.move(source, targetDir.resolve(source.getFileName()), StandardCopyOption.REPLACE_EXISTING);
483 } catch (IOException e) {
484 LOG.warn("Could not move "+spoolFile.getAbsolutePath()+" to "+archiveDir.getAbsolutePath(), e);
492 private SdncArtifactType analyzeFileType(ArtifactTypeEnum artifactType, File spoolFile, Document spoolDoc) {
494 if (artifactType != ArtifactTypeEnum.YANG_XML) {
495 LOG.error("Unexpected artifact type - expecting YANG_XML, got "+artifactType);
496 return SdncArtifactType.UNKNOWN;
504 Element root = spoolDoc.getDocumentElement();
506 String rootName = root.getTagName();
508 if (rootName.contains(":")) {
509 String[] rootNameElems = rootName.split(":");
510 rootName = rootNameElems[rootNameElems.length - 1];
513 if (rootName != null) {
514 SdncArtifactType retValue = SdncArtifactType.fromTag(rootName);
516 if (retValue == SdncArtifactType.UNKNOWN) {
518 LOG.error("Unexpected file contents - root tag is "+rootName);
522 LOG.error("Cannot get root tag from file");
523 return SdncArtifactType.UNKNOWN;
526 } catch (Exception e) {
527 LOG.error("Could not parse YANG_XML file "+spoolFile.getName(), e);
528 return SdncArtifactType.UNKNOWN;
532 private void scheduleDeployment(SdncArtifactType type, String svcName, String resourceName, IArtifactInfo artifactInfo, String spoolFileName, File spoolFile) {
534 if (type.getPass() < deployList.length) {
536 if (artifactInfo != null) {
537 LOG.debug("Scheduling "+artifactInfo.getArtifactName()+" version "+artifactInfo.getArtifactVersion()+" for deployment");
539 deployList[type.getPass()].add(new DeployableArtifact(type, svcName, resourceName, artifactInfo, spoolFile));
541 SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS");//dd/MM/yyyy
542 Date now = new Date();
543 String artifactVersion = sdfDate.format(now);
544 LOG.debug("Scheduling "+spoolFileName+" version "+artifactVersion+" for deployment");
545 String artifactName = spoolFileName;
546 if (artifactInfo != null) {
547 artifactName = artifactInfo.getArtifactName();
549 deployList[type.getPass()].add(new DeployableArtifact(type, svcName, resourceName, artifactName, artifactVersion, spoolFile));
552 LOG.info("Pass for type "+type.getTag()+" is "+type.getPass()+" which is not <= "+deployList.length);
557 private DistributionStatusEnum deploySpoolFile(DeployableArtifact artifact) {
559 DistributionStatusEnum deployResult = DistributionStatusEnum.DEPLOY_OK;
561 StringBuffer msgBuffer = new StringBuffer();
564 String namespace = config.getAsdcApiNamespace();
565 if (namespace == null || namespace.length() == 0) {
566 namespace="com:att:sdnctl:asdcapi";
569 msgBuffer.append("<input xmlns='");
570 msgBuffer.append(namespace);
571 msgBuffer.append("'>\n");
573 String svcName = artifact.getSvcName();
574 String resourceName = artifact.getResourceName();
575 String artifactName = artifact.getArtifactName();
577 if (svcName != null) {
578 if (resourceName != null) {
579 artifactName = svcName + "/" + resourceName + "/" + artifactName;
581 artifactName = svcName + "/" + artifactName;
585 msgBuffer.append("<artifact-name>"+artifactName+"</artifact-name>\n");
586 msgBuffer.append("<artifact-version>"+artifact.getArtifactVersion()+"</artifact-version>\n");
590 BufferedReader rdr = new BufferedReader(new FileReader(artifact.getFile()));
592 String curLine = rdr.readLine();
594 while (curLine != null) {
596 if (!curLine.startsWith("<?")) {
597 msgBuffer.append(curLine+"\n");
599 curLine = rdr.readLine();
603 } catch (Exception e) {
604 LOG.error("Could not process spool file "+artifact.getFile().getName(), e);
605 return DistributionStatusEnum.DEPLOY_ERROR;
608 msgBuffer.append("</input>\n");
611 byte[] msgBytes = msgBuffer.toString().getBytes();
613 Document results = postRestXml(artifact.getType().getRpcUrl(config.getAsdcApiBaseUrl()), msgBytes);
615 if (results == null) {
617 deployResult = DistributionStatusEnum.DEPLOY_ERROR;
620 XPathFactory xpf = XPathFactory.newInstance();
621 XPath xp = xpf.newXPath();
623 String asdcApiResponseCode = "500";
627 asdcApiResponseCode = xp.evaluate("//asdc-api-response-code[position()=1]/text()", results.getDocumentElement());
628 } catch (Exception e) {
629 LOG.error("Caught exception retrying to evaluate xpath", e);
632 if (asdcApiResponseCode.contains("200")) {
633 LOG.info("Update to SDN-C succeeded");
634 deployResult = DistributionStatusEnum.DEPLOY_OK;
636 LOG.info("Update to SDN-C failed (response code "+asdcApiResponseCode+")");
638 if (asdcApiResponseCode.contains("409")) {
639 deployResult = DistributionStatusEnum.ALREADY_DEPLOYED;
642 deployResult = DistributionStatusEnum.DEPLOY_ERROR;
656 public static IDistributionStatusMessage buildStatusMessage(
657 final IDistributionClient client, final INotificationData data,
658 final IArtifactInfo relevantArtifact,
659 final DistributionStatusEnum status) {
660 IDistributionStatusMessage statusMessage = new IDistributionStatusMessage() {
663 public long getTimestamp() {
664 long currentTimeMillis = System.currentTimeMillis();
665 return currentTimeMillis;
669 public DistributionStatusEnum getStatus() {
674 public String getDistributionID() {
675 return data.getDistributionID();
679 public String getConsumerID() {
680 return client.getConfiguration().getConsumerID();
684 public String getArtifactURL() {
685 return relevantArtifact.getArtifactURL();
688 return statusMessage;
692 private HttpURLConnection getRestXmlConnection(String urlString, String method) throws IOException
694 URL sdncUrl = new URL(urlString);
695 Authenticator.setDefault(new SdncAuthenticator(config.getSdncUser(), config.getSdncPasswd()));
697 HttpURLConnection conn = (HttpURLConnection) sdncUrl.openConnection();
699 String authStr = config.getSdncUser()+":"+config.getSdncPasswd();
700 String encodedAuthStr = new String(Base64.encodeBase64(authStr.getBytes()));
702 conn.addRequestProperty("Authentication", "Basic "+encodedAuthStr);
704 conn.setRequestMethod(method);
705 conn.setRequestProperty("Content-Type", "application/xml");
706 conn.setRequestProperty("Accept", "application/xml");
708 conn.setDoInput(true);
709 conn.setDoOutput(true);
710 conn.setUseCaches(false);
716 private Document postRestXml(String urlString, byte[] msgBytes) {
717 Document response = null;
719 LOG.info("Sending REST POST to "+urlString);
720 LOG.info("Message body:\n"+new String(msgBytes));
723 HttpURLConnection conn = getRestXmlConnection(urlString, "POST");
725 if (conn instanceof HttpsURLConnection) {
726 HostnameVerifier hostnameVerifier = new HostnameVerifier() {
728 public boolean verify(String hostname, SSLSession session) {
732 ((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
736 conn.setRequestProperty("Content-Length", ""+msgBytes.length);
737 DataOutputStream outStr = new DataOutputStream(conn.getOutputStream());
738 outStr.write(msgBytes);
743 BufferedReader respRdr;
745 LOG.info("Response: "+conn.getResponseCode()+" "+conn.getResponseMessage());
748 if (conn.getResponseCode() < 300) {
750 respRdr = new BufferedReader(new InputStreamReader(conn.getInputStream()));
752 respRdr = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
755 StringBuffer respBuff = new StringBuffer();
759 while ((respLn = respRdr.readLine()) != null) {
760 respBuff.append(respLn+"\n");
764 String respString = respBuff.toString();
766 LOG.info("Response body :\n"+respString);
768 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
769 DocumentBuilder db = dbf.newDocumentBuilder();
772 response = db.parse(new ByteArrayInputStream(respString.getBytes()));
774 } catch (Exception e) {
776 LOG.error("Caught exception in postRestXml", e);
782 private File applyXslts(File srcFile) {
787 File inFile = srcFile;
790 String xsltPathList = config.getXsltPathList();
792 if (xsltPathList == null || xsltPathList.length() == 0) {
796 String[] xsltPaths = xsltPathList.split(",");
798 for (String xsltPath : xsltPaths) {
801 outFile = File.createTempFile("tmp", "xml");
802 TransformerFactory factory = TransformerFactory.newInstance();
803 Source xslt = new StreamSource(new File(xsltPath));
804 Transformer transformer = factory.newTransformer(xslt);
805 Source text = new StreamSource(inFile);
808 transformer.transform(text, new StreamResult(outFile));
812 } catch (Exception e) {
813 LOG.error("Caught exception trying to apply XSLT template "+xsltPath, e);
820 // After transformations, parse transformed XML
826 private String escapeFilename(String str) {
827 StringBuffer retval = new StringBuffer();
829 for (int i = 0 ; i < str.length() ; i++) {
830 char curchar = str.charAt(i);
831 if (Character.isJavaIdentifierPart(curchar)) {
832 retval.append(curchar);
836 return retval.toString();