diff options
Diffstat (limited to 'plugins')
311 files changed, 32214 insertions, 0 deletions
diff --git a/plugins/LICENSE.txt b/plugins/LICENSE.txt new file mode 100644 index 000000000..3ea5081a0 --- /dev/null +++ b/plugins/LICENSE.txt @@ -0,0 +1,22 @@ +/* + * ============LICENSE_START========================================== + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * =================================================================== + * 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============================================ + * + * ECOMP and OpenECOMP are trademarks + * and service marks of AT&T Intellectual Property. + * + */ diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 000000000..05e536b16 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,20 @@ +This source repository contains the code for the CCSDK plugins. + +To compile this code: + +1. Make sure your local Maven settings file ($HOME/.m2/settings.xml) contains references to the ONAP repositories and OpenDaylight repositories. See example-settings.xml for an example. + +2. To compile, run "mvn clean install". + + +PropertyNode: +1) Takes any file then parses it and puts it to the context memory for Directed Graphs access. +2) Various parameters it takes: + + public String fileName; //Name of the file to put to properties + + public String contextPrefix; //Any prefix to add for your keys in the Properties context + + public Set<String> listNameList;//only applies to XML based file parsing, you can use this to exclude a specific tree to be put to context. + + public boolean fileBasedParsing;//enable to do a file based parsing, currently supports JSON and XML.
\ No newline at end of file diff --git a/plugins/artifacts/pom.xml b/plugins/artifacts/pom.xml new file mode 100755 index 000000000..fd52035b6 --- /dev/null +++ b/plugins/artifacts/pom.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sli-plugins-artifacts</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: sli-plugins-artifacts</name> + <description>CCSDK plugins components Bill of Materials (BOM)</description> + <url>https://wiki.onap.org</url> + <organization> + <name>ONAP</name> + </organization> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-properties-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>feature</classifier> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>feature</classifier> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restconf-client</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>feature</classifier> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-template-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>feature</classifier> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>fabric-discovery-plugin-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>fabric-discovery-plugin-installer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-installer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-installer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client-installer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node-installer</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </dependencyManagement> +</project> diff --git a/plugins/features/ccsdk-gr-toolkit/pom.xml b/plugins/features/ccsdk-gr-toolkit/pom.xml new file mode 100644 index 000000000..21464ef94 --- /dev/null +++ b/plugins/features/ccsdk-gr-toolkit/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-gr-toolkit</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: gr-toolkit :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>odl-mdsal-broker</artifactId> + <type>xml</type> + <classifier>features</classifier> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>gr-toolkit-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>gr-toolkit-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/plugins/features/ccsdk-properties-node/pom.xml b/plugins/features/ccsdk-properties-node/pom.xml new file mode 100644 index 000000000..e11366986 --- /dev/null +++ b/plugins/features/ccsdk-properties-node/pom.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-properties-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: properties-node :: ${project.artifactId}</name> + + <dependencies> + + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>properties-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> +</project> diff --git a/plugins/features/ccsdk-restapi-call-node/pom.xml b/plugins/features/ccsdk-restapi-call-node/pom.xml new file mode 100644 index 000000000..c5dcf096e --- /dev/null +++ b/plugins/features/ccsdk-restapi-call-node/pom.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restapi-call-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: restapi-call-node :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>restapi-call-node-provider</artifactId> + <version>${project.version}</version> + <exclusions> + <!-- let the filter module start and manage the common code --> + <exclusion> + <groupId>org.onap.logging-analytics</groupId> + <artifactId>logging-filter-base</artifactId> + </exclusion> + </exclusions> + </dependency> + </dependencies> +</project> diff --git a/plugins/features/ccsdk-restconf-client/pom.xml b/plugins/features/ccsdk-restconf-client/pom.xml new file mode 100644 index 000000000..3d2e6d0e8 --- /dev/null +++ b/plugins/features/ccsdk-restconf-client/pom.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restconf-client</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: restconf-client :: ${project.artifactId}</name> + + <properties> + <skip.karaf.featureTest>true</skip.karaf.featureTest> + </properties> + + <dependencies> + + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>restconf-client-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/plugins/features/ccsdk-sli-plugins-all/pom.xml b/plugins/features/ccsdk-sli-plugins-all/pom.xml new file mode 100644 index 000000000..fb73584f5 --- /dev/null +++ b/plugins/features/ccsdk-sli-plugins-all/pom.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-sli-plugins-all</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: features :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-properties-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-restapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-restconf-client</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-template-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-sshapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + </dependencies> +</project> diff --git a/plugins/features/ccsdk-sshapi-call-node/pom.xml b/plugins/features/ccsdk-sshapi-call-node/pom.xml new file mode 100644 index 000000000..0441c12cd --- /dev/null +++ b/plugins/features/ccsdk-sshapi-call-node/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-sshapi-call-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: sshapi-call-node :: ${project.artifactId}</name> + <!-- + + <dependencies> + + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>ccsdk-sli</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sshapi-call-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + --> +</project> diff --git a/plugins/features/ccsdk-template-node/pom.xml b/plugins/features/ccsdk-template-node/pom.xml new file mode 100644 index 000000000..4c6721643 --- /dev/null +++ b/plugins/features/ccsdk-template-node/pom.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-template-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: template-node :: ${project.artifactId}</name> + + <dependencies> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>template-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/plugins/features/features-sli-plugins/pom.xml b/plugins/features/features-sli-plugins/pom.xml new file mode 100644 index 000000000..f91c1bd56 --- /dev/null +++ b/plugins/features/features-sli-plugins/pom.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>feature-repo-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>features-sli-plugins</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>feature</packaging> + + <name>ccsdk-sli-plugins :: features :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-gr-toolkit</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-properties-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ccsdk-sli-plugins-all</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + + </dependencies> +</project> diff --git a/plugins/features/installer/pom.xml b/plugins/features/installer/pom.xml new file mode 100755 index 000000000..f58851eca --- /dev/null +++ b/plugins/features/installer/pom.xml @@ -0,0 +1,213 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sliplugins-features-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: features :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-sli-plugins-all</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-gr-toolkit</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-properties-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-restconf-client</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-sshapi-call-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>ccsdk-template-node</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>${application.name}</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + + + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/features/installer/src/assembly/assemble_installer_zip.xml b/plugins/features/installer/src/assembly/assemble_installer_zip.xml new file mode 100755 index 000000000..a6a22a9ba --- /dev/null +++ b/plugins/features/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,39 @@ +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>bin</id> + + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/features/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/features/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100755 index 000000000..86b07f1f3 --- /dev/null +++ b/plugins/features/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,29 @@ +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/features/installer/src/main/resources/scripts/install-feature.sh b/plugins/features/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..e16e79a12 --- /dev/null +++ b/plugins/features/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -d ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/features/pom.xml b/plugins/features/pom.xml new file mode 100755 index 000000000..e95874196 --- /dev/null +++ b/plugins/features/pom.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sliplugins-feature-aggregator</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: features</name> + + <modules> + <module>ccsdk-gr-toolkit</module> + <module>ccsdk-properties-node</module> + <module>ccsdk-restapi-call-node</module> + <module>ccsdk-restconf-client</module> + <module>ccsdk-sshapi-call-node</module> + <module>ccsdk-template-node</module> + <module>ccsdk-sli-plugins-all</module> + <module>features-sli-plugins</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/grToolkit/.gitignore b/plugins/grToolkit/.gitignore new file mode 100755 index 000000000..a01e90efe --- /dev/null +++ b/plugins/grToolkit/.gitignore @@ -0,0 +1,38 @@ +tandard .git ignore entries##### + +## IDE Specific Files ## +org.eclipse.core.resources.prefs +.classpath +.project +.settings +.idea +.externalToolBuilders +maven-eclipse.xml +workspace + +## Compilation Files ## +*.class +**/target +target +target-ide +MANIFEST.MF + +## Misc Ignores (OS specific etc) ## +bin/ +dist +*~ +*.ipr +*.iml +*.iws +classes +out/ +.DS_STORE +.metadata + +## Folders which contain auto generated source code ## +yang-gen-config +yang-gen-sal + +#####Archetype specific .git ignore entries####### +generate +Archetype_Next_Steps.README diff --git a/plugins/grToolkit/README.md b/plugins/grToolkit/README.md new file mode 100755 index 000000000..84594496b --- /dev/null +++ b/plugins/grToolkit/README.md @@ -0,0 +1,316 @@ +Introduction +====================== +You have generated an MD-SAL module. + +* You should be able to successfully run ```mvn clean install``` on this project. + +Next Steps +====================== +* Run a ```mvn clean install``` if you haven't already. This will generate some code from the yang models. +* Modify the model yang file under the model project. +* Follow the comments in the generated provider class to wire your new provider into the generated +code. +* Modify the generated provider model to respond to and handle the yang model. Depending on what +you added to your model you may need to inherit additional interfaces or make other changes to +the provider model. + +Generated Bundles +====================== +* model + - Provides the yang model for your application. This is your primary northbound interface. +* provider + - Provides a template implementation for a provider to respond to your yang model. +* features + - Defines a karaf feature. If you add dependencies on third-party bundles then you will need to + modify the features.xml to list out the dependencies. +* installer + - Bundles all of the jars and third party dependencies (minus ODL dependencies) into a single + .zip file. + +Usage +====================== +## Purpose +The purpose of this ODL feature is to support geo-redundancy through a series of ODL integrated health checks and tools. + +## Properties File +On initialization gr-toolkit expects to find a file named ```gr-toolkit.properties``` located in the ```SDNC_CONFIG``` directory. The properties file should contain: +- ```akka.conf.location``` + - The path to the akka.conf configuration file. +- ```adm.useSsl``` + - true/false; Determines whether or not to use http or https when making requests to the Admin Portal. +- ```adm.fqdn``` + - The FQDN or url of the site's Admin Portal. +- ```adm.healthcheck``` + - The url path of the Admin Portal's health check page. +- ```adm.port.http``` + - The HTTP port for the Admin Portal. +- ```adm.port.ssl``` + - The HTTPS port for the Admin Portal. +- ```controller.credentials``` + - username:password; The credentials used to make requests to the ODL controller. +- ```controller.useSsl``` + - true/false; Determines whether or not to use http or https when making requests to the controller. +- ```controller.port.http``` + - The HTTP port for the ODL Controller. +- ```controller.port.ssl``` + - The HTTPS port for the ODL Controller. +- ```controller.port.akka``` + - The port used for Akka communications on the ODL Controller. +- ```mbean.cluster``` + - The Jolokia path for the Akka Cluster MBean. +- ```mbean.shardManager``` + - The Jolokia path for the Akka ShardManager MBean. +- ```mbean.shard.config``` + - The Jolokia path for the Akka Shard MBean. This should be templated to look like ```/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore```. GR Toolkit will use this template with information pulled from the Akka ShardManager MBean. +- ```site.identifier``` + - A unique identifier for the site the ODL Controller resides on. + +## Site Identifier +Returns a unique site identifier of the site the ODL resides on. + +> ### Input: None +> +> ### Output +> ```json +> { +> "output": { +> "id": "UNIQUE_IDENTIFIER_HERE", +> "status": "200" +> } +> } +> ``` + +## Admin Health +Returns HEALTHY/FAULTY based on whether or not a 200 response is received from the Admin Portal's health check page. + +> ### Input: None +> +> ### Output +> ```json +> { +> "output": { +> "status": "200", +> "health": "HEALTHY" +> } +> } +> ``` + +## Database Health +Returns HEALTHY/FAULTY based on if DbLib can obtain a writeable connection from its pool. + +> ### Input: None +> +> ### Output +> ```json +> { +> "output": { +> "status": "200", +> "health": "HEALTHY" +> } +> } +> ``` + +## Cluster Health +Uses Jolokia queries to determine shard health and voting status. In a 3 ODL node configuration, 2 FAULTY nodes constitutes a FAULTY site. In a 6 node configuration it is assumed that there are 2 sites consiting of 3 nodes each. + +> ### Input: None +> +> ### Output +> ```json +> { +> "output": { +> "site1-health": "HEALTHY", +> "members": [ +> { +> "address": "member-3.node", +> "role": "member-3", +> "unreachable": false, +> "voting": true, +> "up": true, +> "replicas": [ +> { +> "shard": "member-3-shard-default-config" +> } +> ] +> }, +> { +> "address": "member-1.node", +> "role": "member-1", +> "unreachable": false, +> "voting": true, +> "up": true, +> "replicas": [ +> { +> "shard": "member-1-shard-default-config" +> } +> ] +> }, +> { +> "address": "member-5.node", +> "role": "member-5", +> "unreachable": false, +> "voting": false, +> "up": true, +> "replicas": [ +> { +> "shard": "member-5-shard-default-config" +> } +> ] +> }, +> { +> "address": "member-2.node", +> "role": "member-2", +> "unreachable": false, +> "leader": [ +> { +> "shard": "member-2-shard-default-config" +> } +> ], +> "commit-status": [ +> { +> "shard": "member-5-shard-default-config", +> "delta": 148727 +> }, +> { +> "shard": "member-4-shard-default-config", +> "delta": 148869 +> } +> ], +> "voting": true, +> "up": true, +> "replicas": [ +> { +> "shard": "member-2-shard-default-config" +> } +> ] +> }, +> { +> "address": "member-4.node", +> "role": "member-4", +> "unreachable": false, +> "voting": false, +> "up": true, +> "replicas": [ +> { +> "shard": "member-4-shard-default-config" +> } +> ] +> }, +> { +> "address": "member-6.node", +> "role": "member-6", +> "unreachable": false, +> "voting": false, +> "up": true, +> "replicas": [ +> { +> "shard": "member-6-shard-default-config" +> } +> ] +> } +> ], +> "status": "200", +> "site2-health": "HEALTHY" +> } +> } +> ``` + +## Site Health +Aggregates data from Admin Health, Database Health, and Cluster Health and returns a simplified payload containing the health of a site. A FAULTY Admin Portal or Database health status will constitute a FAULTY site; in a 3 ODL node configuration, 2 FAULTY nodes constitutes a FAULTY site. If any portion of the health check registers as FAULTY, the entire site will be designated as FAULTY. In a 6 node configuration these health checks are performed cross site as well. + +> ### Input: None +> +> ### Output +> ```json +> { +> "output": { +> "sites": [ +> { +> "id": "SITE_1", +> "role": "ACTIVE", +> "health": "HEALTHY" +> }, +> { +> "id": "SITE_2", +> "role": "STANDBY", +> "health": "FAULTY" +> } +> ], +> "status": "200" +> } +> } +> ``` + +## Halt Akka Traffic +Places rules in IP Tables to block Akka traffic to/from a specific node on a specified port. + +> ### Input: +> ```json +> { +> "input": { +> "node-info": [ +> { +> "node": "your.odl.node", +> "port": "2550" +> } +> ] +> } +> } +> ``` +> +> ### Output +> ```json +> { +> "output": { +> "status": "200" +> } +> } +> ``` + +## Resume Akka Traffic +Removes rules in IP Tables to allow Akka traffic to/from a specifc node on a specified port. + +> ### Input: +> ```json +> { +> "input": { +> "node-info": [ +> { +> "node": "your.odl.node", +> "port": "2550" +> } +> ] +> } +> } +> ``` +> +> ### Output +> ```json +> { +> "output": { +> "status": "200" +> } +> } +> ``` + +## Failover +Only usable in a 6 ODL node configuration. Determines which site is active/standby, switches voting to the standby site, and isolates the old active site. If backupData=true an MD-SAL export will be scheduled and backed up to a Nexus server (requires ccsdk.sli.northbound.daexim-offsite-backup feature). + +> ### Input: +> ```json +> { +> "input": { +> "backupData": "true" +> } +> } +> ``` +> +> ### Output +> ```json +> { +> "output": { +> "status": "200", +> "message": "Failover complete." +> } +> } +> ```
\ No newline at end of file diff --git a/plugins/grToolkit/installer/pom.xml b/plugins/grToolkit/installer/pom.xml new file mode 100755 index 000000000..d0392a65d --- /dev/null +++ b/plugins/grToolkit/installer/pom.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: gr-toolkit :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-gr-toolkit</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-model</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/plugins/grToolkit/installer/src/assembly/assemble_installer_zip.xml b/plugins/grToolkit/installer/src/assembly/assemble_installer_zip.xml new file mode 100755 index 000000000..41d23e88a --- /dev/null +++ b/plugins/grToolkit/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,56 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> +</assembly> diff --git a/plugins/grToolkit/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/grToolkit/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100755 index 000000000..fe7a90ce5 --- /dev/null +++ b/plugins/grToolkit/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,45 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + </fileSet> + </fileSets> + +</assembly> diff --git a/plugins/grToolkit/installer/src/main/resources/scripts/install-feature.sh b/plugins/grToolkit/installer/src/main/resources/scripts/install-feature.sh new file mode 100755 index 000000000..1d7be149e --- /dev/null +++ b/plugins/grToolkit/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2018 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}-repo.zip + +if [ -f ${REPOZIP} ] +then + unzip -d ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/grToolkit/model/.gitignore b/plugins/grToolkit/model/.gitignore new file mode 100755 index 000000000..eacf31a67 --- /dev/null +++ b/plugins/grToolkit/model/.gitignore @@ -0,0 +1 @@ +/target-ide/ diff --git a/plugins/grToolkit/model/pom.xml b/plugins/grToolkit/model/pom.xml new file mode 100755 index 000000000..5d518d744 --- /dev/null +++ b/plugins/grToolkit/model/pom.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-model</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: gr-toolkit :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId> + <artifactId>rfc6991</artifactId> + </dependency> + + + </dependencies> +</project> diff --git a/plugins/grToolkit/model/scripts/python/yang2props.py b/plugins/grToolkit/model/scripts/python/yang2props.py new file mode 100755 index 000000000..85daccfbd --- /dev/null +++ b/plugins/grToolkit/model/scripts/python/yang2props.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2018 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +import re +import sys + + +# Convert word from foo-bar to FooBar +# words begining with a digit will be converted to _digit +def to_enum(s): + if s[0].isdigit(): + s = "_" + s + else: + s = s[0].upper() + s[1:] + return re.sub(r'(?!^)-([a-zA-Z])', lambda m: m.group(1).upper(), s) + +leaf = "" +val = "" +li = [] + +if len(sys.argv) < 3: + print 'yang2props.py <input yang> <output properties>' + sys.exit(2) + +with open(sys.argv[1], "r") as ins: + for line in ins: + # if we see a leaf save the name for later + if "leaf " in line: + match = re.search(r'leaf (\S+)', line) + if match: + leaf = match.group(1) + + # if we see enum convert the value to enum format and see if it changed + # if the value is different write a property entry + if "enum " in line: + match = re.search(r'enum "(\S+)";', line) + if match: + val = match.group(1) + enum = to_enum(val) + + # see if converting to enum changed the string + if val != enum: + property = "yang."+leaf+"."+enum+"="+val + if property not in li: + li.append( property) + + +# Open output file +fo = open(sys.argv[2], "wb") +fo.write("# yang conversion properties \n") +fo.write("# used to convert Enum back to the original yang value \n") +fo.write("\n".join(li)) +fo.write("\n") + +# Close opend file +fo.close() + + diff --git a/plugins/grToolkit/model/src/main/yang/gr-toolkit.yang b/plugins/grToolkit/model/src/main/yang/gr-toolkit.yang new file mode 100755 index 000000000..cc21400a7 --- /dev/null +++ b/plugins/grToolkit/model/src/main/yang/gr-toolkit.yang @@ -0,0 +1,199 @@ +module gr-toolkit{
+ namespace "org:onap:ccsdk:sli:plugins:gr-toolkit";
+ prefix gr-toolkit;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import ietf-yang-types {
+ prefix yang;
+ }
+ description
+ "This ODL feature is designed to gauge the health of all cluster members.";
+ revision "2018-09-26" {
+ description
+ "Release 19.02 draft";
+ }
+
+ grouping member {
+ leaf address {
+ type string;
+ mandatory true;
+ }
+ leaf role {
+ type string;
+ mandatory true;
+ }
+ leaf up {
+ type boolean;
+ mandatory true;
+ }
+ leaf unreachable {
+ type boolean;
+ mandatory true;
+ }
+ leaf voting {
+ type boolean;
+ mandatory true;
+ }
+ list leader {
+ leaf shard {
+ type string;
+ mandatory true;
+ }
+ }
+ list replicas {
+ leaf shard {
+ type string;
+ mandatory true;
+ }
+ }
+ list commit-status {
+ leaf shard {
+ type string;
+ mandatory true;
+ }
+ leaf delta {
+ type int32;
+ mandatory true;
+ }
+ }
+ }
+
+ grouping site {
+ leaf id {
+ type string;
+ mandatory true;
+ }
+ leaf role {
+ type string;
+ mandatory true;
+ }
+ leaf health {
+ type string;
+ mandatory true;
+ }
+ }
+
+ grouping node {
+ leaf node {
+ type string;
+ mandatory true;
+ }
+ leaf port {
+ type string;
+ mandatory true;
+ }
+ }
+
+ rpc cluster-health {
+ description
+ "Parses akka.conf for seed nodes and queries Jolokia for the health
+ of each node.";
+ output {
+ leaf status { type string; }
+ leaf message { type string; }
+ leaf served-by { type string; }
+ leaf site1-health { type string; }
+ leaf site2-health { type string; }
+ list members {
+ uses member;
+ }
+ }
+ }
+
+ rpc database-health {
+ description
+ "Uses DbLibService connection info to determine if the database is reachable.";
+ output {
+ leaf status { type string; }
+ leaf health { type string; }
+ leaf served-by { type string; }
+ }
+ }
+
+ rpc admin-health {
+ description
+ "Pings the admin portal health check to determine if the admin portal is reachable.";
+ output {
+ leaf status { type string; }
+ leaf health { type string; }
+ leaf served-by { type string; }
+ }
+ }
+
+ rpc site-health {
+ description
+ "Gathers health information on the ODL cluster, database, and admin portal
+ to determine if the entire site is in a healthy state.";
+ output {
+ leaf status { type string; }
+ leaf served-by { type string; }
+ list sites {
+ uses site;
+ }
+ }
+ }
+
+ rpc site-identifier {
+ description
+ "Returns the unique site identifier.";
+ output {
+ leaf status { type string; }
+ leaf served-by { type string; }
+ leaf id { type string; mandatory true; }
+ }
+ }
+
+ rpc halt-akka-traffic {
+ description
+ "Executes IP Tables commands to halt traffic to the akka port.";
+ input {
+ list node-info {
+ uses node;
+ }
+ }
+ output {
+ leaf status { type string; }
+ leaf served-by { type string; }
+ }
+ }
+
+ rpc resume-akka-traffic {
+ description
+ "Executes IP Tables commands to resume traffic to the akka port.";
+ input {
+ list node-info {
+ uses node;
+ }
+ }
+ output {
+ leaf status { type string; }
+ leaf served-by { type string; }
+ }
+ }
+
+ rpc failover {
+ description
+ "Performs a failover from primary site to standby site.";
+ input {
+ leaf backupData {
+ type string;
+ default "false";
+ }
+ leaf isolate {
+ type string;
+ default "false";
+ }
+ leaf downUnreachable {
+ type string;
+ default "false";
+ }
+ }
+ output {
+ leaf status { type string; }
+ leaf message { type string; }
+ leaf served-by { type string; }
+ }
+ }
+}
diff --git a/plugins/grToolkit/pom.xml b/plugins/grToolkit/pom.xml new file mode 100755 index 000000000..499876b3b --- /dev/null +++ b/plugins/grToolkit/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: gr-toolkit</name> + <description>ODL bundle used to judge health of SDN-C application.</description> + + <modules> + <module>model</module> + <module>installer</module> + <module>provider</module> + </modules> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </dependencyManagement> +</project> diff --git a/plugins/grToolkit/provider/.gitignore b/plugins/grToolkit/provider/.gitignore new file mode 100755 index 000000000..eacf31a67 --- /dev/null +++ b/plugins/grToolkit/provider/.gitignore @@ -0,0 +1 @@ +/target-ide/ diff --git a/plugins/grToolkit/provider/pom.xml b/plugins/grToolkit/provider/pom.xml new file mode 100755 index 000000000..2fc981b46 --- /dev/null +++ b/plugins/grToolkit/provider/pom.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: gr-toolkit :: ${project.artifactId}</name> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>gr-toolkit-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-binding-api</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>dblib-provider</artifactId> + <version>${ccsdk.sli.core.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <version>${ccsdk.sli.core.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-provider</artifactId> + <version>${ccsdk.sli.core.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>annotations</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-test-model</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-binding-broker-impl</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-binding-broker-impl</artifactId> + <type>test-jar</type> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <!-- WireMock used to create test web server --> + <!-- Using same dependency found in MSO Utils --> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-contract-wiremock</artifactId> + <version>1.2.4.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-distributed-datastore</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.typesafe.akka</groupId> + <artifactId>akka-cluster_2.12</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.github.stefanbirkner</groupId> + <artifactId>system-rules</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package>org.opendaylight.controller.config.yang.config.gr-toolkit_provider</Export-Package> + <Import-Package>*</Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java new file mode 100755 index 000000000..7a8b3deb0 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProvider.java @@ -0,0 +1,765 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.annotation.Nonnull; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import org.apache.commons.lang.StringUtils; + +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionManager; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionResponse; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.MemberBuilder; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.PropertyKeys; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver; +import org.onap.ccsdk.sli.plugins.grtoolkit.resolver.SingleNodeHealthResolver; +import org.onap.ccsdk.sli.plugins.grtoolkit.resolver.SixNodeHealthResolver; +import org.onap.ccsdk.sli.plugins.grtoolkit.resolver.ThreeNodeHealthResolver; + +import org.json.JSONArray; +import org.json.JSONObject; + +import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.GrToolkitService; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Member; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.Site; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierInput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.site.health.output.SitesBuilder; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * API implementation of the {@code GrToolkitService} interface generated from + * the gr-toolkit.yang model. The RPCs contained within this class are meant to + * run in an architecture agnostic fashion, where the response is repeatable + * and predictable across any given node configuration. To facilitate this, + * health checking and failover logic has been abstracted into the + * {@code HealthResolver} classes. + * <p> + * Anyone who wishes to write a custom resolver for use with GR Toolkit should + * extend the {@code HealthResolver} class. The currently provided resolvers + * are useful references for further implementation. + * + * @author Anthony Haddox + * @see GrToolkitService + * @see HealthResolver + * @see SingleNodeHealthResolver + * @see ThreeNodeHealthResolver + * @see SixNodeHealthResolver + */ +public class GrToolkitProvider implements AutoCloseable, GrToolkitService, DataTreeChangeListener { + private static final String APP_NAME = "gr-toolkit"; + private static final String PROPERTIES_FILE = System.getenv("SDNC_CONFIG_DIR") + "/gr-toolkit.properties"; + private String akkaConfig; + private String httpProtocol; + private String siteIdentifier = System.getenv("SITE_NAME"); + private final Logger log = LoggerFactory.getLogger(GrToolkitProvider.class); + private final ExecutorService executor; + protected DataBroker dataBroker; + protected NotificationPublishService notificationService; + protected RpcProviderRegistry rpcRegistry; + protected BindingAwareBroker.RpcRegistration<GrToolkitService> rpcRegistration; + protected DbLibService dbLib; + private String member; + private ClusterActor self; + private HashMap<String, ClusterActor> memberMap; + private Properties properties; + private DistributedDataStoreInterface configDatastore; + private HealthResolver resolver; + + /** + * Constructs the provider for the GR Toolkit API. Dependencies are + * injected using the GrToolkit.xml blueprint. + * + * @param dataBroker The Data Broker + * @param notificationProviderService The Notification Service + * @param rpcProviderRegistry The RPC Registry + * @param configDatastore The Configuration Data Store provided by the controller + * @param dbLibService Reference to the controller provided DbLibService + */ + public GrToolkitProvider(DataBroker dataBroker, + NotificationPublishService notificationProviderService, + RpcProviderRegistry rpcProviderRegistry, + DistributedDataStoreInterface configDatastore, + DbLibService dbLibService) { + log.info("Creating provider for {}", APP_NAME); + this.executor = Executors.newFixedThreadPool(1); + this.dataBroker = dataBroker; + this.notificationService = notificationProviderService; + this.rpcRegistry = rpcProviderRegistry; + this.configDatastore = configDatastore; + this.dbLib = dbLibService; + initialize(); + } + + /** + * Initializes some structures necessary to hold health check information + * and perform failovers. + */ + private void initialize() { + log.info("Initializing provider for {}", APP_NAME); + createContainers(); + setProperties(); + defineMembers(); + rpcRegistration = rpcRegistry.addRpcImplementation(GrToolkitService.class, this); + log.info("Initialization complete for {}", APP_NAME); + } + + /** + * Creates the {@code Properties} object with the contents of + * gr-toolkit.properties, found at the {@code SDNC_CONFIG_DIR} directory, + * which should be set as an environment variable. If the properties file + * is not found, GR Toolkit will not function. + */ + private void setProperties() { + log.info("Loading properties from {}", PROPERTIES_FILE); + properties = new Properties(); + File propertiesFile = new File(PROPERTIES_FILE); + if(!propertiesFile.exists()) { + log.warn("setProperties(): Properties file not found."); + } else { + try(FileInputStream fileInputStream = new FileInputStream(propertiesFile)) { + properties.load(fileInputStream); + if(!properties.containsKey(PropertyKeys.SITE_IDENTIFIER)) { + properties.put(PropertyKeys.SITE_IDENTIFIER, "Unknown Site"); + } + httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://"; + akkaConfig = properties.getProperty(PropertyKeys.AKKA_CONF_LOCATION).trim(); + if(StringUtils.isEmpty(siteIdentifier)) { + siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim(); + } + log.info("setProperties(): Loaded properties."); + } catch(IOException e) { + log.error("setProperties(): Error loading properties.", e); + } + } + } + + /** + * Parses the akka.conf file used by the controller to define an akka + * cluster. This method requires the <i>seed-nodes</i> definition to exist + * on a single line. + */ + private void defineMembers() { + member = configDatastore.getActorUtils().getCurrentMemberName().getName(); + log.info("defineMembers(): Cluster member: {}", member); + + log.info("defineMembers(): Parsing akka.conf for cluster memberMap..."); + try { + File akkaConfigFile = new File(this.akkaConfig); + try(FileReader fileReader = new FileReader(akkaConfigFile); + BufferedReader bufferedReader = new BufferedReader(fileReader)) { + String line; + while((line = bufferedReader.readLine()) != null) { + if(line.contains("seed-nodes =")) { + parseSeedNodes(line); + break; + } + } + } + } catch(IOException e) { + log.error("defineMembers(): Couldn't load akka", e); + } catch(NullPointerException e) { + log.error("defineMembers(): akkaConfig is null. Check properties file and restart {} bundle.", APP_NAME); + log.error("defineMembers(): NullPointerException", e); + } + log.info("self:\n{}", self); + } + + /** + * Sets up the {@code InstanceIdentifier}s for Data Store transactions. + */ + private void createContainers() { + // Replace with MD-SAL write for FailoverStatus + } + + /** + * Shuts down the {@code ExecutorService} and closes the RPC Provider Registry. + */ + @Override + public void close() throws Exception { + log.info("Closing provider for {}", APP_NAME); + executor.shutdown(); + rpcRegistration.close(); + log.info("close(): Successfully closed provider for {}", APP_NAME); + } + + /** + * Listens for changes to the Data tree. + * + * @param changes Data tree changes. + */ + @Override + public void onDataTreeChanged(@Nonnull Collection changes) { + log.info("onDataTreeChanged(): No changes."); + } + + /** + * Makes a call to {@code resolver.getClusterHealth()} to determine the + * health of the akka clustered controllers. + * + * @param input request body adhering to the model for + * {@code ClusterHealthInput} + * @return response adhering to the model for {@code ClusterHealthOutput} + * @see HealthResolver + * @see ClusterHealthInput + * @see ClusterHealthOutput + */ + @Override + public ListenableFuture<RpcResult<ClusterHealthOutput>> clusterHealth(ClusterHealthInput input) { + log.info("{}:cluster-health invoked.", APP_NAME); + resolver.getClusterHealth(); + return buildClusterHealthOutput(); + } + + /** + * Makes a call to {@code resolver.getSiteHealth()} to determine the health + * of all of the application components of a site. In a multi-site config, + * this will gather the health of all sites. + * + * @param input request body adhering to the model for + * {@code SiteHealthInput} + * @return response adhering to the model for {@code SiteHealthOutput} + * @see HealthResolver + * @see SiteHealthInput + * @see SiteHealthOutput + */ + @Override + public ListenableFuture<RpcResult<SiteHealthOutput>> siteHealth(SiteHealthInput input) { + log.info("{}:site-health invoked.", APP_NAME); + List<SiteHealth> sites = resolver.getSiteHealth(); + return buildSiteHealthOutput(sites); + } + + /** + * Makes a call to {@code resolver.getDatabaseHealth()} to determine the + * health of the database(s) used by the controller. + * + * @param input request body adhering to the model for + * {@code DatabaseHealthInput} + * @return response adhering to the model for {@code DatabaseHealthOutput} + * @see HealthResolver + * @see DatabaseHealthInput + * @see DatabaseHealthOutput + */ + @Override + public ListenableFuture<RpcResult<DatabaseHealthOutput>> databaseHealth(DatabaseHealthInput input) { + log.info("{}:database-health invoked.", APP_NAME); + DatabaseHealthOutputBuilder outputBuilder = new DatabaseHealthOutputBuilder(); + DatabaseHealth health = resolver.getDatabaseHealth(); + outputBuilder.setStatus(health.getHealth().equals(Health.HEALTHY) ? "200" : "500"); + outputBuilder.setHealth(health.getHealth().toString()); + outputBuilder.setServedBy(member); + log.info("databaseHealth(): Health: {}", health.getHealth()); + return Futures.immediateFuture(RpcResultBuilder.<DatabaseHealthOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Makes a call to {@code resolver.getAdminHealth()} to determine the + * health of the administrative portal(s) used by the controller. + * + * @param input request body adhering to the model for + * {@code AdminHealthInput} + * @return response adhering to the model for {@code AdminHealthOutput} + * @see HealthResolver + * @see AdminHealthInput + * @see AdminHealthOutput + */ + @Override + public ListenableFuture<RpcResult<AdminHealthOutput>> adminHealth(AdminHealthInput input) { + log.info("{}:admin-health invoked.", APP_NAME); + AdminHealthOutputBuilder outputBuilder = new AdminHealthOutputBuilder(); + AdminHealth adminHealth = resolver.getAdminHealth(); + outputBuilder.setStatus(Integer.toString(adminHealth.getStatusCode())); + outputBuilder.setHealth(adminHealth.getHealth().toString()); + outputBuilder.setServedBy(member); + log.info("adminHealth(): Status: {} | Health: {}", adminHealth.getStatusCode(), adminHealth.getHealth()); + return Futures.immediateFuture(RpcResultBuilder.<AdminHealthOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Places IP Tables rules in place to drop akka communications traffic with + * one or mode nodes. This method does not not perform any checks to see if + * rules currently exist, and assumes success. + * + * @param input request body adhering to the model for + * {@code HaltAkkaTrafficInput} + * @return response adhering to the model for {@code HaltAkkaTrafficOutput} + * @see HaltAkkaTrafficInput + * @see HaltAkkaTrafficOutput + */ + @Override + public ListenableFuture<RpcResult<HaltAkkaTrafficOutput>> haltAkkaTraffic(HaltAkkaTrafficInput input) { + log.info("{}:halt-akka-traffic invoked.", APP_NAME); + HaltAkkaTrafficOutputBuilder outputBuilder = new HaltAkkaTrafficOutputBuilder(); + outputBuilder.setStatus("200"); + modifyIpTables(IpTables.ADD, input.getNodeInfo().toArray()); + outputBuilder.setServedBy(member); + + return Futures.immediateFuture(RpcResultBuilder.<HaltAkkaTrafficOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Removes IP Tables rules in place to permit akka communications traffic + * with one or mode nodes. This method does not not perform any checks to + * see if rules currently exist, and assumes success. + * + * @param input request body adhering to the model for + * {@code ResumeAkkaTrafficInput} + * @return response adhering to the model for {@code ResumeAkkaTrafficOutput} + * @see ResumeAkkaTrafficInput + * @see ResumeAkkaTrafficOutput + */ + @Override + public ListenableFuture<RpcResult<ResumeAkkaTrafficOutput>> resumeAkkaTraffic(ResumeAkkaTrafficInput input) { + log.info("{}:resume-akka-traffic invoked.", APP_NAME); + ResumeAkkaTrafficOutputBuilder outputBuilder = new ResumeAkkaTrafficOutputBuilder(); + outputBuilder.setStatus("200"); + modifyIpTables(IpTables.DELETE, input.getNodeInfo().toArray()); + outputBuilder.setServedBy(member); + + return Futures.immediateFuture(RpcResultBuilder.<ResumeAkkaTrafficOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Returns a canned response containing the identifier for this + * controller's site. + * + * @param input request body adhering to the model for + * {@code SiteIdentifierInput} + * @return response adhering to the model for {@code SiteIdentifierOutput} + * @see SiteIdentifierInput + * @see SiteIdentifierOutput + */ + @Override + public ListenableFuture<RpcResult<SiteIdentifierOutput>> siteIdentifier(SiteIdentifierInput input) { + log.info("{}:site-identifier invoked.", APP_NAME); + SiteIdentifierOutputBuilder outputBuilder = new SiteIdentifierOutputBuilder(); + outputBuilder.setStatus("200"); + outputBuilder.setId(siteIdentifier); + outputBuilder.setServedBy(member); + return Futures.immediateFuture(RpcResultBuilder.<SiteIdentifierOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Makes a call to {@code resolver.tryFailover()} to try a failover defined + * by the active {@code HealthResolver}. + * + * @param input request body adhering to the model for + * {@code FailoverInput} + * @return response adhering to the model for {@code FailoverOutput} + * @see HealthResolver + * @see FailoverInput + * @see FailoverOutput + */ + @Override + public ListenableFuture<RpcResult<FailoverOutput>> failover(FailoverInput input) { + log.info("{}:failover invoked.", APP_NAME); + FailoverOutputBuilder outputBuilder = new FailoverOutputBuilder(); + FailoverStatus failoverStatus = resolver.tryFailover(input); + outputBuilder.setServedBy(member); + outputBuilder.setMessage(failoverStatus.getMessage()); + outputBuilder.setStatus(Integer.toString(failoverStatus.getStatusCode())); + log.info("{}:{}.", APP_NAME, failoverStatus.getMessage()); + return Futures.immediateFuture(RpcResultBuilder.<FailoverOutput>status(true).withResult(outputBuilder.build()).build()); + } + + /** + * Performs an akka traffic isolation of the active site from the standby + * site in an Active/Standby architecture. Invokes the + * {@code halt-akka-traffic} RPC against the standby site nodes using the + * information of the active site nodes. + * + * @param activeSite list of nodes in the active site + * @param standbySite list of nodes in the standby site + * @param port http or https port of the controller + * @deprecated No longer used since the refactor to use the HealthResolver + * pattern. Retained so the logic can be replicated later. + */ + @Deprecated + private void isolateSiteFromCluster(ArrayList<ClusterActor> activeSite, ArrayList<ClusterActor> standbySite, String port) { + log.info("isolateSiteFromCluster(): Halting Akka traffic..."); + for(ClusterActor actor : standbySite) { + try { + log.info("Halting Akka traffic for: {}", actor.getNode()); + // Build JSON with activeSite actor Node and actor AkkaPort + JSONObject akkaInput = new JSONObject(); + JSONObject inputBlock = new JSONObject(); + JSONArray votingStateArray = new JSONArray(); + JSONObject nodeInfo; + for(ClusterActor node : activeSite) { + nodeInfo = new JSONObject(); + nodeInfo.put("node", node.getNode()); + nodeInfo.put("port", node.getAkkaPort()); + votingStateArray.put(nodeInfo); + } + inputBlock.put("node-info", votingStateArray); + akkaInput.put("input", inputBlock); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/gr-toolkit:halt-akka-traffic", ConnectionManager.HttpMethod.POST, akkaInput.toString(), ""); + } catch(IOException e) { + log.error("isolateSiteFromCluster(): Could not halt Akka traffic for: " + actor.getNode(), e); + } + } + } + + /** + * Invokes the down unreachable action through the Jolokia mbean API. + * + * @param activeSite list of nodes in the active site + * @param standbySite list of nodes in the standby site + * @param port http or https port of the controller + * @deprecated No longer used since the refactor to use the HealthResolver + * pattern. Retained so the logic can be replicated later. + */ + @Deprecated + private void downUnreachableNodes(ArrayList<ClusterActor> activeSite, ArrayList<ClusterActor> standbySite, String port) { + log.info("downUnreachableNodes(): Setting site unreachable..."); + JSONObject jolokiaInput = new JSONObject(); + jolokiaInput.put("type", "EXEC"); + jolokiaInput.put("mbean", "akka:type=Cluster"); + jolokiaInput.put("operation", "down"); + JSONArray arguments = new JSONArray(); + for(ClusterActor actor : activeSite) { + // Build Jolokia input + // May need to change from akka port to actor.getAkkaPort() + arguments.put("akka.tcp://opendaylight-cluster-data@" + actor.getNode() + ":" + properties.getProperty(PropertyKeys.CONTROLLER_PORT_AKKA)); + } + jolokiaInput.put("arguments", arguments); + log.debug("downUnreachableNodes(): {}", jolokiaInput); + try { + log.info("downUnreachableNodes(): Setting nodes unreachable"); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + standbySite.get(0).getNode() + ":" + port + "/jolokia", ConnectionManager.HttpMethod.POST, jolokiaInput.toString(), ""); + } catch(IOException e) { + log.error("downUnreachableNodes(): Error setting nodes unreachable", e); + } + } + + /** + * Triggers a data backup and export sequence of MD-SAL data. Invokes the + * {@code data-export-import:schedule-export} RPC to schedule a data export + * and subsequently the {@code daexim-offsite-backup:backup-data} RPC + * against the active site to export and backup the data. Assumes the + * controllers have the org.onap.ccsdk.sli.northbound.daeximoffsitebackup + * bundle installed. + * + * @param activeSite list of nodes in the active site + * @param port http or https port of the controller + * @deprecated No longer used since the refactor to use the HealthResolver + * pattern. Retained so the logic can be replicated later. + */ + @Deprecated + private void backupMdSal(ArrayList<ClusterActor> activeSite, String port) { + log.info("backupMdSal(): Backing up data..."); + try { + log.info("backupMdSal(): Scheduling backup for: {}", activeSite.get(0).getNode()); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + activeSite.get(0).getNode() + ":" + port + "/restconf/operations/data-export-import:schedule-export", ConnectionManager.HttpMethod.POST, "{ \"input\": { \"run-at\": \"30\" } }", ""); + } catch(IOException e) { + log.error("backupMdSal(): Error backing up MD-SAL", e); + } + for(ClusterActor actor : activeSite) { + try { + // Move data offsite + log.info("backupMdSal(): Backing up data for: {}", actor.getNode()); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + actor.getNode() + ":" + port + "/restconf/operations/daexim-offsite-backup:backup-data", ConnectionManager.HttpMethod.POST, null, ""); + } catch(IOException e) { + log.error("backupMdSal(): Error backing up data.", e); + } + } + } + + /** + * Builds a response object for {@code clusterHealth()}. Sorts and iterates + * over the contents of the {@code memberMap}, which contains the health + * information of the cluster, and adds them to the {@code outputBuilder}. + * If the ClusterActor is healthy, according to + * {@code resolver.isControllerHealthy()}, the {@code ClusterHealthOutput} + * status has a {@code 0} appended, otherwise a {@code 1} is appended. A + * status of all zeroes denotes a healthy cluster. This status should be + * easily decoded by tools which use the output. + * + * @return future containing a completed {@code ClusterHealthOutput} + * @see ClusterActor + * @see ClusterHealthOutput + * @see HealthResolver + */ + @SuppressWarnings("unchecked") + private ListenableFuture<RpcResult<ClusterHealthOutput>> buildClusterHealthOutput() { + ClusterHealthOutputBuilder outputBuilder = new ClusterHealthOutputBuilder(); + outputBuilder.setServedBy(member); + List memberList = new ArrayList<Member>(); + StringBuilder stat = new StringBuilder(); + memberMap.values() + .stream() + .sorted(Comparator.comparingInt(member -> Integer.parseInt(member.getMember().split("-")[1]))) + .forEach(member -> { + memberList.add(new MemberBuilder(member).build()); + // 0 is a healthy controller, 1 is unhealthy. + // The list is sorted so users can decode to find unhealthy nodes + // This will also let them figure out health on a per-site basis + // Depending on any tools they use with this API + if(resolver.isControllerHealthy(member)) { + stat.append("0"); + } else { + stat.append("1"); + } + }); + outputBuilder.setStatus(stat.toString()); + outputBuilder.setMembers(memberList); + RpcResult<ClusterHealthOutput> rpcResult = RpcResultBuilder.<ClusterHealthOutput>status(true).withResult(outputBuilder.build()).build(); + return Futures.immediateFuture(rpcResult); + } + + /** + * Builds a response object for {@code siteHealth()}. Iterates over a list + * of {@code SiteHealth} objects and populates the {@code SiteHealthOutput} + * with the information. + * + * @param sites list of sites + * @return future containing a completed {@code SiteHealthOutput} + * @see SiteHealth + * @see HealthResolver + */ + @SuppressWarnings("unchecked") + private ListenableFuture<RpcResult<SiteHealthOutput>> buildSiteHealthOutput(List<SiteHealth> sites) { + SiteHealthOutputBuilder outputBuilder = new SiteHealthOutputBuilder(); + SitesBuilder siteBuilder = new SitesBuilder(); + outputBuilder.setStatus("200"); + outputBuilder.setSites((List) new ArrayList<Site>()); + + for(SiteHealth site : sites) { + siteBuilder.setHealth(site.getHealth().toString()); + siteBuilder.setRole(site.getRole()); + siteBuilder.setId(site.getId()); + outputBuilder.getSites().add(siteBuilder.build()); + log.info("buildSiteHealthOutput(): Health for {}: {}", site.getId(), site.getHealth().getHealth()); + } + + outputBuilder.setServedBy(member); + RpcResult<SiteHealthOutput> rpcResult = RpcResultBuilder.<SiteHealthOutput>status(true).withResult(outputBuilder.build()).build(); + return Futures.immediateFuture(rpcResult); + } + + /** + * Parses a line containing the akka networking information of the akka + * controller cluster. Assumes entries of the format: + * <p> + * akka.tcp://opendaylight-cluster-data@<FQDN>:<AKKA_PORT> + * <p> + * The information is stored in a {@code ClusterActor} object, and then + * added to the memberMap HashMap, with the {@code FQDN} as the key. The + * final step is a call to {@code createHealthResolver} to create the + * health resolver for the provider. + * + * @param line the line containing all of the seed nodes + * @see ClusterActor + * @see HealthResolver + */ + private void parseSeedNodes(String line) { + memberMap = new HashMap<>(); + line = line.substring(line.indexOf("[\""), line.indexOf(']')); + String[] splits = line.split(","); + + for(int ndx = 0; ndx < splits.length; ndx++) { + String nodeName = splits[ndx]; + int delimLocation = nodeName.indexOf('@'); + String port = nodeName.substring(splits[ndx].indexOf(':', delimLocation) + 1, splits[ndx].indexOf('"', splits[ndx].indexOf(':'))); + splits[ndx] = nodeName.substring(delimLocation + 1, splits[ndx].indexOf(':', delimLocation)); + log.info("parseSeedNodes(): Adding node: {}:{}", splits[ndx], port); + ClusterActor clusterActor = new ClusterActor(); + clusterActor.setNode(splits[ndx]); + clusterActor.setAkkaPort(port); + clusterActor.setMember("member-" + (ndx + 1)); + if(member.equals(clusterActor.getMember())) { + self = clusterActor; + } + memberMap.put(clusterActor.getNode(), clusterActor); + log.info("parseSeedNodes(): {}", clusterActor); + } + + createHealthResolver(); + } + + /** + * Creates the specific health resolver requested by the user, as specified + * in the gr-toolkit.properties file. If a resolver is not specified, or + * there is an issue creating the resolver, it will use a fallback resolver + * based on how many nodes are added to the memberMap HashMap. + * + * @see HealthResolver + * @see SingleNodeHealthResolver + * @see ThreeNodeHealthResolver + * @see SixNodeHealthResolver + */ + private void createHealthResolver() { + log.info("createHealthResolver(): Creating health resolver..."); + try { + Class resolverClass = null; + String userDefinedResolver = properties.getProperty(PropertyKeys.RESOLVER); + if(StringUtils.isEmpty(userDefinedResolver)) { + throw new InstantiationException(); + } + resolverClass = Class.forName(userDefinedResolver); + Class[] types = { Map.class , properties.getClass(), DbLibService.class }; + Constructor<HealthResolver> constructor = resolverClass.getConstructor(types); + Object[] parameters = { memberMap, properties, dbLib }; + resolver = constructor.newInstance(parameters); + log.info("createHealthResolver(): Created resolver from name {}", resolver.toString()); + } catch(ClassNotFoundException | InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + log.warn("createHealthResolver(): Could not create user defined resolver", e); + if(memberMap.size() == 1) { + log.info("createHealthResolver(): FALLBACK: Initializing SingleNodeHealthResolver..."); + resolver = new SingleNodeHealthResolver(memberMap, properties, dbLib); + } else if(memberMap.size() == 3) { + log.info("createHealthResolver(): FALLBACK: Initializing ThreeNodeHealthResolver..."); + resolver = new ThreeNodeHealthResolver(memberMap, properties, dbLib); + } else if(memberMap.size() == 6) { + log.info("createHealthResolver(): FALLBACK: Initializing SixNodeHealthResolver..."); + resolver = new SixNodeHealthResolver(memberMap, properties, dbLib); + } + } + } + + /** + * Adds or drops IPTables rules to block or resume akka traffic for a node + * in the akka cluster. Assumes that the user or group that the controller + * is run as has the ability to run sudo /sbin/iptables without requiring a + * password. This method will run indefinitely if that assumption is not + * correct. This method does not check to see if any rules around the node + * are preexisting, so multiple uses will result in multiple additions and + * removals from IPTables. + * + * @param task the operation to be performed against IPTables + * @param nodeInfo array containing the nodes to be added or dropped from + * IPTables + */ + private void modifyIpTables(IpTables task, Object[] nodeInfo) { + log.info("modifyIpTables(): Modifying IPTables rules..."); + if(task == IpTables.ADD) { + for(Object node : nodeInfo) { + org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo n = + (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.halt.akka.traffic.input.NodeInfo) node; + log.info("modifyIpTables(): Isolating {}", n.getNode()); + executeCommand(String.format("sudo /sbin/iptables -A INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); + executeCommand(String.format("sudo /sbin/iptables -A OUTPUT -p tcp --destination-port %s -j DROP -d %s", n.getPort(), n.getNode())); + } + } else if(task == IpTables.DELETE) { + for(Object node : nodeInfo) { + org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo n = + (org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.resume.akka.traffic.input.NodeInfo) node; + log.info("modifyIpTables(): De-isolating {}", n.getNode()); + executeCommand(String.format("sudo /sbin/iptables -D INPUT -p tcp --destination-port %s -j DROP -s %s", properties.get(PropertyKeys.CONTROLLER_PORT_AKKA), n.getNode())); + executeCommand(String.format("sudo /sbin/iptables -D OUTPUT -p tcp --destination-port %s -j DROP -d %s", n.getPort(), n.getNode())); + } + } + if(nodeInfo.length > 0) { + executeCommand("sudo /sbin/iptables -L"); + } + } + + /** + * Opens a shell session and executes a command. + * + * @param command the shell command to execute + */ + private void executeCommand(String command) { + log.info("executeCommand(): Executing command: {}", command); + String[] cmd = command.split(" "); + try { + Process p = Runtime.getRuntime().exec(cmd); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String inputLine; + StringBuilder content = new StringBuilder(); + while((inputLine = bufferedReader.readLine()) != null) { + content.append(inputLine); + } + bufferedReader.close(); + log.info("executeCommand(): {}", content); + } catch(IOException e) { + log.error("executeCommand(): Error executing command", e); + } + } + + /** + * The IPTables operations this module can perform. + */ + enum IpTables { + ADD, + DELETE + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManager.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManager.java new file mode 100644 index 000000000..99fcd3e04 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManager.java @@ -0,0 +1,157 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.connection; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +/** + * Handles the process for getting HTTP connections to resources. Has the + * ability to send JSON payloads. Only supports basic authorization when + * sending credentials. + * + * @author Anthony Haddox + * @see ConnectionResponse + */ +public interface ConnectionManager { + Logger log = LoggerFactory.getLogger(ConnectionManager.class); + int CONNECTION_TIMEOUT = 5000; // 5 second timeout + enum HttpMethod { + GET("GET"), + POST("POST"); + + private final String method; + HttpMethod(String method) { + this.method = method; + } + String getMethod() { + return method; + } + } + + /** + * Writes a JSON payload to an {@code HTTPURLConnection OutputStream}. + * + * @param input the JSON payload to send + * @param connection the {@code HTTPURLConnection} to write to + * @throws IOException if there is a problem writing to the output stream + */ + static void sendPayload(String input, HttpURLConnection connection) throws IOException { + byte[] out = input.getBytes(StandardCharsets.UTF_8); + int length = out.length; + + connection.setFixedLengthStreamingMode(length); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + connection.connect(); + try(OutputStream os = connection.getOutputStream()) { + os.write(out); + } + } + + /** + * Gets an {@code HTTPURLConnection} to a {@code host}. + * + * @param host the host to connect to + * @return an {@code HTTPURLConnection} + * @throws IOException if a connection cannot be opened + */ + static HttpURLConnection getConnection(String host) throws IOException { + log.info("getConnection(): Getting connection to: {}", host); + URL url = new URL(host); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("Connection", "keep-alive"); + connection.setRequestProperty("Proxy-Connection", "keep-alive"); + connection.setConnectTimeout(CONNECTION_TIMEOUT); + connection.setReadTimeout(CONNECTION_TIMEOUT); + return connection; + } + + /** + * Gets an {@code HTTPURLConnection} to a {@code host} and sets the + * Authorization header with the supplied credentials. Only supports basic + * authentication. + * + * @param host the host to connect to + * @param credentials the authorization credentials + * @return an {@code HTTPURLConnection} with Authorization header set + * @throws IOException if a connection cannot be opened + */ + static HttpURLConnection getConnection(String host, String credentials) throws IOException { + String auth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(credentials.getBytes()); + HttpURLConnection connection = getConnection(host); + connection.addRequestProperty("Authorization", auth); + credentials = null; + auth = null; + return connection; + } + + /** + * Opens a connection to a path, sends a payload (if supplied with one), + * and returns the response. + * @param path the host to connect to + * @param method the {@code HttpMethod} to use + * @param input the payload to send + * @param credentials the credentials to use + * @return a {@code ConnectionResponse} containing the response body and + * status code of the operation + * @throws IOException if a connection cannot be opened or if the payload + * cannot be sent + * @see HttpMethod + */ + static ConnectionResponse getConnectionResponse(String path, HttpMethod method, String input, String credentials) throws IOException { + HttpURLConnection connection = (StringUtils.isEmpty(credentials)) ? getConnection(path) : getConnection(path, credentials); + credentials = null; + connection.setRequestMethod(method.getMethod()); + connection.setDoInput(true); + + if(!StringUtils.isEmpty(input)) { + sendPayload(input, connection); + } + + StringBuilder content = new StringBuilder(); + try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String inputLine; + while((inputLine = bufferedReader.readLine()) != null) { + content.append(inputLine); + } + } finally { + connection.disconnect(); + } + + ConnectionResponse connectionResponse = new ConnectionResponse(); + connectionResponse.content = content.toString(); + connectionResponse.statusCode = connection.getResponseCode(); + log.info("getConnectionResponse(): {} response code from {}", connectionResponse.statusCode, path); + log.debug("getConnectionResponse(): Response:\n{}", connectionResponse.content); + return connectionResponse; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponse.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponse.java new file mode 100644 index 000000000..fb16d2a12 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponse.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.connection; + +/** + * A data container for HTTP connection requests. + * + * @author Anthony Haddox + * @see ConnectionManager + */ +public class ConnectionResponse { + public int statusCode; + public String content; + + public ConnectionResponse withStatusCode(int statusCode) { + this.statusCode = statusCode; + return this; + } + + @Override + public String toString() { + return "Status: " + statusCode + "\nContent: " + content; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealth.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealth.java new file mode 100644 index 000000000..0e88df55e --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealth.java @@ -0,0 +1,58 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +/** + * A data container for Admin health. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver + */ +public class AdminHealth { + private Health health; + private int statusCode; + + public AdminHealth(Health health) { + this.health = health; + } + + public AdminHealth(Health health, int statusCode) { + this.health = health; + this.statusCode = statusCode; + } + + public Health getHealth() { + return health; + } + + public void setHealth(Health health) { + this.health = health; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java new file mode 100644 index 000000000..d039c865e --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterActor.java @@ -0,0 +1,222 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A data container with information about an actor in the Akka cluster. + * + * @author Anthony Haddox + */ +public class ClusterActor { + private String node; + private String member; + private String site; + private String akkaPort; + private boolean voting; + private boolean up; + private boolean unreachable; + private ArrayList<String> shardLeader; + private ArrayList<String> replicaShards; + private ArrayList<String> nonReplicaShards; + private HashMap<String, Integer> commits; + + public static final String SITE_1 = "Site 1"; + public static final String SITE_2 = "Site 2"; + + public ClusterActor() { + node = ""; + member = ""; + site = ""; + voting = false; + up = false; + unreachable = false; + shardLeader = new ArrayList<>(); + replicaShards = new ArrayList<>(); + nonReplicaShards = new ArrayList<>(); + commits = new HashMap<>(); + } + + public String getNode() { + return node; + } + + public void setNode(String node) { + this.node = node; + } + + public String getMember() { + return member; + } + + public void setMember(String member) { + this.member = member; + } + + public String getSite() { + return site; + } + + public void setSite(String site) { + this.site = site; + } + + public String getAkkaPort() { + return akkaPort; + } + + public void setAkkaPort(String akkaPort) { + this.akkaPort = akkaPort; + } + + public boolean isVoting() { + return voting; + } + + public void setVoting(boolean voting) { + this.voting = voting; + } + + public boolean isUp() { + return up; + } + + public void setUp(boolean up) { + this.up = up; + } + + public boolean isUnreachable() { + return unreachable; + } + + public void setUnreachable(boolean unreachable) { + this.unreachable = unreachable; + } + + public List<String> getShardLeader() { + return shardLeader; + } + + public void setShardLeader(List<String> shardLeader) { + this.shardLeader = (ArrayList<String>) shardLeader; + } + + public List<String> getReplicaShards() { + return replicaShards; + } + + public void setReplicaShards(List<String> replicaShards) { + this.replicaShards = (ArrayList<String>) replicaShards; + } + + public List<String> getNonReplicaShards() { + return nonReplicaShards; + } + + public void setNonReplicaShards(List<String> nonReplicaShards) { + this.nonReplicaShards = (ArrayList<String>) nonReplicaShards; + } + + public Map<String, Integer> getCommits() { + return commits; + } + + public void setCommits(Map<String, Integer> commits) { + this.commits = (HashMap<String, Integer>) commits; + } + + public void flush() { + shardLeader.clear(); + replicaShards.clear(); + nonReplicaShards.clear(); + commits.clear(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("[ "); + builder.append(this.member); + builder.append(" ] "); + + builder.append(this.node); + builder.append(":"); + builder.append(this.akkaPort); + builder.append(" is"); + if(up) + builder.append(" Up"); + else + builder.append(" Down"); + if(unreachable) { + builder.append(" [ UNREACHABLE ]"); + return builder.toString(); + } + + if(voting) + builder.append(" (Voting)"); + + builder.append("\n"); + + for(String l : this.shardLeader) { + builder.append("\tLeader: "); + builder.append(l); + builder.append("\n"); + } + + for(String r : this.replicaShards) { + builder.append("\tReplicating: "); + builder.append(r); + builder.append("\n"); + } + + for(String n : this.nonReplicaShards) { + builder.append("\tNot replicating: "); + builder.append(n); + builder.append("\n"); + } + + for(Map.Entry<String, Integer> entry : commits.entrySet()) { + String key = entry.getKey(); + int value = entry.getValue(); + if(value > 0) { + builder.append("\t"); + builder.append(value); + builder.append(" commits ahead of "); + builder.append(key); + builder.append("\n"); + } + else if(value < 0) { + builder.append("\t"); + builder.append(value); + builder.append(" commits behind "); + builder.append(key); + builder.append("\n"); + } + } + + return builder.toString(); + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealth.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealth.java new file mode 100644 index 000000000..52198225d --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealth.java @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +/** + * A data container for Akka Cluster health. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver + */ +public class ClusterHealth { + private Health health; + + public ClusterHealth() { + health = Health.FAULTY; + } + + public ClusterHealth withHealth(Health h) { + this.health = h; + return this; + } + + public Health getHealth() { + return health; + } + + public void setHealth(Health health) { + this.health = health; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealth.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealth.java new file mode 100644 index 000000000..14c11b51a --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealth.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +/** + * A data container for Database health. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver + */ +public class DatabaseHealth { + private Health health; + + public DatabaseHealth(Health health) { + this.health = health; + } + + public Health getHealth() { + return health; + } + + public void setHealth(Health health) { + this.health = health; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatus.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatus.java new file mode 100644 index 000000000..7366ede43 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatus.java @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +/** + * A data container for the status of a controller-level failover. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver + */ +public class FailoverStatus { + private int statusCode; + private String message; + + public FailoverStatus() { + this.statusCode = 200; + this.message = "Failover complete."; + } + + public FailoverStatus withStatusCode(int code) { + this.statusCode = code; + return this; + } + + public FailoverStatus withMessage(String message) { + this.message = message; + return this; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/Health.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/Health.java new file mode 100644 index 000000000..deb5cb444 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/Health.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +/** + * Potential health values. + * + * @author Anthony Haddox + */ +public enum Health { + HEALTHY("HEALTHY"), + FAULTY("FAULTY"); + + private final String health; + Health(String health) { + this.health = health; + } + public String getHealth() { + return health; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java new file mode 100644 index 000000000..863b56674 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilder.java @@ -0,0 +1,88 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.cluster.health.output.MembersBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.CommitStatusBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.ReplicasBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.member.LeaderBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Extends the {@code MembersBuilder} generated from the gr-toolkit.yang model. + * Uses information from a {@code ClusterActor} to populate the builder fields. + * + * @author Anthony Haddox + * @see ClusterActor + * @see org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.cluster.health.output.MembersBuilder + */ +public class MemberBuilder extends MembersBuilder { + public MemberBuilder(ClusterActor actor) { + super(); + this.setAddress(actor.getNode()); + this.setRole(actor.getMember()); + this.setVoting(actor.isVoting()); + this.setUp(actor.isUp()); + this.setUnreachable(actor.isUnreachable()); + populateReplicas(actor.getReplicaShards()); + populateCommits(actor.getCommits()); + populateLeader(actor.getShardLeader()); + } + + private void populateLeader(List<String> shardLeader) { + LeaderBuilder builder; + this.setLeader(new ArrayList<>()); + for(String leader : shardLeader) { + builder = new LeaderBuilder(); + builder.setShard(leader); + this.getLeader().add(builder.build()); + } + } + + private void populateCommits(Map<String, Integer> commits) { + CommitStatusBuilder builder; + this.setCommitStatus(new ArrayList<>()); + for(Map.Entry<String, Integer> entry : commits.entrySet()) { + String key = entry.getKey(); + Integer value = entry.getValue(); + if(value != 0) { + builder = new CommitStatusBuilder(); + builder.setShard(key); + builder.setDelta(value); + this.getCommitStatus().add(builder.build()); + } + } + } + + private void populateReplicas(List<String> replicaShards) { + ReplicasBuilder builder; + this.setReplicas(new ArrayList<>()); + for(String shard : replicaShards) { + builder = new ReplicasBuilder(); + builder.setShard(shard); + this.getReplicas().add(builder.build()); + } + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/PropertyKeys.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/PropertyKeys.java new file mode 100644 index 000000000..f2ad90a7f --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/PropertyKeys.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +public interface PropertyKeys { + String RESOLVER = "resolver"; + String SITE_IDENTIFIER = "site.identifier"; + String CONTROLLER_USE_SSL = "controller.useSsl"; + String CONTROLLER_PORT_SSL = "controller.port.ssl"; + String CONTROLLER_PORT_HTTP = "controller.port.http"; + String CONTROLLER_PORT_AKKA = "controller.port.akka"; + String CONTROLLER_CREDENTIALS = "controller.credentials"; + String AKKA_CONF_LOCATION = "akka.conf.location"; + String MBEAN_CLUSTER = "mbean.cluster"; + String MBEAN_SHARD_MANAGER = "mbean.shardManager"; + String MBEAN_SHARD_CONFIG = "mbean.shard.config"; + String ADM_USE_SSL = "adm.useSsl"; + String ADM_PORT_SSL = "adm.port.ssl"; + String ADM_PORT_HTTP = "adm.port.http"; + String ADM_FQDN = "adm.fqdn"; + String ADM_HEALTHCHECK= "adm.healthcheck"; +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealth.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealth.java new file mode 100644 index 000000000..8407032d5 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealth.java @@ -0,0 +1,125 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +/** + * A data container for Site health. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.resolver.HealthResolver + */ +public class SiteHealth { + private List<AdminHealth> adminHealth; + private List<DatabaseHealth> databaseHealth; + private List<ClusterHealth> clusterHealth; + + private Health health; + private String id; + private String role; + + public SiteHealth() { + adminHealth = new ArrayList<>(); + databaseHealth = new ArrayList<>(); + clusterHealth = new ArrayList<>(); + + // Faulty by default, it's up to the health check to affirm the health + health = Health.FAULTY; + } + + public SiteHealth withAdminHealth(AdminHealth... health) { + Collections.addAll(adminHealth, health); + return this; + } + + public SiteHealth withDatabaseHealth(DatabaseHealth... health) { + Collections.addAll(databaseHealth, health); + return this; + } + + public SiteHealth withClusterHealth(ClusterHealth... health) { + Collections.addAll(clusterHealth, health); + return this; + } + + public SiteHealth withId(String id) { + this.id = id; + return this; + } + + public SiteHealth withRole(String role) { + this.role = role; + return this; + } + + public Health getHealth() { + return health; + } + + public void setHealth(Health health) { + this.health = health; + } + + public List<AdminHealth> getAdminHealth() { + return adminHealth; + } + + public void setAdminHealth(List<AdminHealth> adminHealth) { + this.adminHealth = adminHealth; + } + + public List<DatabaseHealth> getDatabaseHealth() { + return databaseHealth; + } + + public void setDatabaseHealth(List<DatabaseHealth> databaseHealth) { + this.databaseHealth = databaseHealth; + } + + public List<ClusterHealth> getClusterHealth() { + return clusterHealth; + } + + public void setClusterHealth(List<ClusterHealth> clusterHealth) { + this.clusterHealth = clusterHealth; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/HealthResolver.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/HealthResolver.java new file mode 100644 index 000000000..5c4ed13d7 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/HealthResolver.java @@ -0,0 +1,212 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import org.json.JSONException; +import org.json.JSONObject; + +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionManager; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionResponse; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.PropertyKeys; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static org.onap.ccsdk.sli.plugins.grtoolkit.data.Health.HEALTHY; + +/** + * Abstract class for the Health Resolver system, which allows for custom logic + * to be implemented, while leaving inputs/outputs generic and architecture + * agnostic. This class provides some simple implementations of both Admin and + * Database health checking, but leaves cluster and site health determinations + * up to the implementer. Useful implementation examples can be found in the + * {@code SingleNodeHealthResolver}, {@code ThreeNodeHealthResolver}, and + * {@code SixNodeHealthResolver} classes. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see SingleNodeHealthResolver + * @see ThreeNodeHealthResolver + * @see SixNodeHealthResolver + */ +public abstract class HealthResolver { + private final Logger log = LoggerFactory.getLogger(HealthResolver.class); + static final String OUTPUT = "output"; + final String httpProtocol; + final String controllerPort; + final String credentials; + final Map<String, ClusterActor> memberMap; + private DbLibService dbLib; + final ShardResolver shardResolver; + private String adminPath; + private String siteIdentifier; + + /** + * Constructs the health resolver used by the {@code GrToolkitProvider} to + * determine the health of the application components. + * + * @param map a HashMap containing all of the nodes in the akka cluster + * @param properties the properties passed ino the provider + * @param dbLib a reference to the {@code DbLibService} of the provider + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + */ + HealthResolver(Map<String, ClusterActor> map, Properties properties, DbLibService dbLib) { + log.info("Creating {}", this.getClass().getCanonicalName()); + this.memberMap = map; + this.dbLib = dbLib; + shardResolver = ShardResolver.getInstance(properties); + + String adminProtocol = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? "https://" : "http://"; + String adminPort = "true".equals(properties.getProperty(PropertyKeys.ADM_USE_SSL)) ? properties.getProperty(PropertyKeys.ADM_PORT_SSL) : properties.getProperty(PropertyKeys.ADM_PORT_HTTP); + adminPath = adminProtocol + properties.getProperty(PropertyKeys.ADM_FQDN) + ":" + adminPort + properties.getProperty(PropertyKeys.ADM_HEALTHCHECK); + siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim(); + + controllerPort = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL).trim() : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP).trim(); + httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://"; + if(siteIdentifier == null || siteIdentifier.isEmpty()) { + siteIdentifier = properties.getProperty(PropertyKeys.SITE_IDENTIFIER).trim(); + } + credentials = properties.getProperty(PropertyKeys.CONTROLLER_CREDENTIALS).trim(); + } + + public abstract ClusterHealth getClusterHealth(); + public abstract List<SiteHealth> getSiteHealth(); + public abstract FailoverStatus tryFailover(FailoverInput input); + public abstract void resolveSites(); + + /** + * Gets a connection to the admin portal. If the status code is 200, the + * admin portal is assumed to be healthy. + * + * @return an {@code AdminHealth} object with health of the admin portal + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see AdminHealth + */ + public AdminHealth getAdminHealth() { + log.info("getAdminHealth(): Requesting health check from {}", adminPath); + try { + ConnectionResponse response = ConnectionManager.getConnectionResponse(adminPath, ConnectionManager.HttpMethod.GET, null, null); + Health health = (response.statusCode == 200) ? HEALTHY : Health.FAULTY; + AdminHealth adminHealth = new AdminHealth(health, response.statusCode); + log.info("getAdminHealth(): Response: {}", response); + return adminHealth; + } catch(IOException e) { + log.error("getAdminHealth(): Problem getting ADM health.", e); + return new AdminHealth(Health.FAULTY, 500); + } + } + + /** + * Uses {@code DbLibService} to get a connection to the database. If + * {@code DbLibService} is active and the connection it returns is not read + * only, the database(s) is assumed to be healthy. + * + * @return an {@code DatabaseHealth} object with health of the database + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see DatabaseHealth + */ + public DatabaseHealth getDatabaseHealth() { + log.info("getDatabaseHealth(): Determining database health..."); + try (Connection connection = dbLib.getConnection()){ + log.debug("getDatabaseHealth(): DBLib isActive(): {}", dbLib.isActive()); + log.debug("getDatabaseHealth(): DBLib isReadOnly(): {}", connection.isReadOnly()); + log.debug("getDatabaseHealth(): DBLib isClosed(): {}", connection.isClosed()); + if(!dbLib.isActive() || connection.isClosed() || connection.isReadOnly()) { + log.warn("getDatabaseHealth(): Database is FAULTY"); + return new DatabaseHealth(Health.FAULTY); + } + log.info("getDatabaseHealth(): Database is HEALTHY"); + } catch(SQLException e) { + log.error("getDatabaseHealth(): Database is FAULTY"); + log.error("getDatabaseHealth(): Error", e); + return new DatabaseHealth(Health.FAULTY); + } + + return new DatabaseHealth(HEALTHY); + } + + /** + * Utility method to see if an input is healthy. + * + * @return true if the input is healthy + * @see Health + */ + boolean isHealthy(Health h) { + return HEALTHY == h; + } + + public String getSiteIdentifier() { + return siteIdentifier; + } + + public void setSiteIdentifier(String siteIdentifier) { + this.siteIdentifier = siteIdentifier; + } + + /** + * Used to invoke the admin-health or database-health RPC to check if that + * component is healthy. + * + * @param path the path to the admin-health or database-health RPCs + * @return true if the component is healthy + * @throws IOException if a connection cannot be obtained + */ + boolean isRemoteComponentHealthy(String path) throws IOException { + String content = ConnectionManager.getConnectionResponse(path, ConnectionManager.HttpMethod.POST, null, credentials).content; + try { + JSONObject responseJson = new JSONObject(content); + JSONObject responseValue = responseJson.getJSONObject(OUTPUT); + return HEALTHY.toString().equals(responseValue.getString("health")); + } catch(JSONException e) { + log.error("Error parsing JSON", e); + throw new IOException(); + } + } + + /** + * Checks a {@code ClusterActor} object to see if the node is healthy. + * + * @param controller the controller to check + * @return true if the controller is up and reachable + * @see ClusterActor + */ + public boolean isControllerHealthy(ClusterActor controller) { + return (controller.isUp() && ! controller.isUnreachable()); + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ShardResolver.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ShardResolver.java new file mode 100644 index 000000000..8e96bff0c --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ShardResolver.java @@ -0,0 +1,177 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionManager; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionResponse; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.PropertyKeys; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Used to perform operations on the data shard information returned as JSON + * from Jolokia. + * + * @author Anthony Haddox + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + */ +public class ShardResolver { + private final Logger log = LoggerFactory.getLogger(ShardResolver.class); + private static ShardResolver _shardResolver; + + private String jolokiaClusterPath; + private String shardManagerPath; + private String shardPathTemplate; + private String credentials; + private String httpProtocol; + + private static final String VALUE = "value"; + + private ShardResolver(Properties properties) { + String port = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? properties.getProperty(PropertyKeys.CONTROLLER_PORT_SSL).trim() : properties.getProperty(PropertyKeys.CONTROLLER_PORT_HTTP).trim(); + httpProtocol = "true".equals(properties.getProperty(PropertyKeys.CONTROLLER_USE_SSL).trim()) ? "https://" : "http://"; + jolokiaClusterPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_CLUSTER).trim(); + shardManagerPath = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_MANAGER).trim(); + shardPathTemplate = ":" + port + properties.getProperty(PropertyKeys.MBEAN_SHARD_CONFIG).trim(); + credentials = properties.getProperty(PropertyKeys.CONTROLLER_CREDENTIALS).trim(); + } + + public static ShardResolver getInstance(Properties properties) { + if (_shardResolver == null) { + _shardResolver = new ShardResolver(properties); + } + return _shardResolver; + } + + private void getMemberStatus(ClusterActor clusterActor) throws IOException { + log.info("getMemberStatus(): Getting member status for {}", clusterActor.getNode()); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + clusterActor.getNode() + jolokiaClusterPath, ConnectionManager.HttpMethod.GET, null, credentials); + try { + JSONObject responseJson = new JSONObject(response.content); + JSONObject responseValue = responseJson.getJSONObject(VALUE); + clusterActor.setUp("Up".equals(responseValue.getString("MemberStatus"))); + clusterActor.setUnreachable(false); + } catch(JSONException e) { + log.error("getMemberStatus(): Error parsing response from {}", clusterActor.getNode(), e); + clusterActor.setUp(false); + clusterActor.setUnreachable(true); + } + } + + private void getShardStatus(ClusterActor clusterActor) throws IOException { + log.info("getShardStatus(): Getting shard status for {}", clusterActor.getNode()); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + clusterActor.getNode() + shardManagerPath, ConnectionManager.HttpMethod.GET, null, credentials); + try { + JSONObject responseValue = new JSONObject(response.content).getJSONObject(VALUE); + JSONArray shardList = responseValue.getJSONArray("LocalShards"); + + String pattern = "-config$"; + Pattern r = Pattern.compile(pattern); + List<String> shards = new ArrayList<>(); + for(int ndx = 0; ndx < shardList.length(); ndx++) { + shards.add(shardList.getString(ndx)); + } + shards.parallelStream().forEach(shard -> { + Matcher m = r.matcher(shard); + String operationalShardName = m.replaceFirst("-operational"); + String shardConfigPath = String.format(shardPathTemplate, shard); + String shardOperationalPath = String.format(shardPathTemplate, operationalShardName).replace("Config", "Operational"); + try { + extractShardInfo(clusterActor, shard, shardConfigPath); + extractShardInfo(clusterActor, operationalShardName, shardOperationalPath); + } catch(IOException e) { + log.error("getShardStatus(): Error extracting shard info for {}", shard); + } + }); + } catch(JSONException e) { + log.error("getShardStatus(): Error parsing response from " + clusterActor.getNode(), e); + } + } + + private void extractShardInfo(ClusterActor clusterActor, String shardName, String shardPath) throws IOException { + log.info("extractShardInfo(): Extracting shard info for {}", shardName); + String shardPrefix = ""; +// String shardPrefix = clusterActor.getMember() + "-shard-"; + log.debug("extractShardInfo(): Pulling config info for {} from: {}", shardName, shardPath); + ConnectionResponse response = ConnectionManager.getConnectionResponse(httpProtocol + clusterActor.getNode() + shardPath, ConnectionManager.HttpMethod.GET, null, credentials); + log.debug("extractShardInfo(): Response: {}", response.content); + + try { + JSONObject shardValue = new JSONObject(response.content).getJSONObject(VALUE); + clusterActor.setVoting(shardValue.getBoolean("Voting")); + if(shardValue.getString("PeerAddresses").length() > 0) { + clusterActor.getReplicaShards().add(shardName.replace(shardPrefix, "")); + if(shardValue.getString("Leader").startsWith(clusterActor.getMember())) { + clusterActor.getShardLeader().add(shardName.replace(shardPrefix, "")); + } + } else { + clusterActor.getNonReplicaShards().add(shardName.replace(shardPrefix, "")); + } + JSONArray followerInfo = shardValue.getJSONArray("FollowerInfo"); + for(int followerNdx = 0; followerNdx < followerInfo.length(); followerNdx++) { + int commitIndex = shardValue.getInt("CommitIndex"); + int matchIndex = followerInfo.getJSONObject(followerNdx).getInt("matchIndex"); + if(commitIndex != -1 && matchIndex != -1) { + int commitsBehind = commitIndex - matchIndex; + clusterActor.getCommits().put(followerInfo.getJSONObject(followerNdx).getString("id"), commitsBehind); + } + } + } catch(JSONException e) { + log.error("extractShardInfo(): Error parsing response from " + clusterActor.getNode(), e); + } + } + + public void getControllerHealth(Map<String, ClusterActor> memberMap) { + memberMap.values().parallelStream().forEach(this::getControllerHealth); + } + + // Seen ConcurrentAccess issues, probably related to getting the controller health + private synchronized void getControllerHealth(ClusterActor clusterActor) { + clusterActor.flush(); + log.info("getControllerHealth(): Gathering info for {}", clusterActor.getNode()); + try { + // First flush out the old values + getMemberStatus(clusterActor); + getShardStatus(clusterActor); + } catch(IOException e) { + log.error("getControllerHealth(): Connection Error", e); + clusterActor.setUnreachable(true); + clusterActor.setUp(false); + } + log.info("getControllerHealth(): MemberInfo:\n{}", clusterActor); + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolver.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolver.java new file mode 100644 index 000000000..2799df1b0 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolver.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Implementation of {@code HealthResolver} for a single node controller + * architecture. + * + * @author Anthony Haddox + * @see HealthResolver + */ +public class SingleNodeHealthResolver extends HealthResolver { + private final Logger log = LoggerFactory.getLogger(SingleNodeHealthResolver.class); + + /** + * Constructs the health resolver used by the {@code GrToolkitProvider} to + * determine the health of the application components. + * + * @param map a HashMap containing all of the nodes in the akka cluster + * @param properties the properties passed ino the provider + * @param dbLib a reference to the {@code DbLibService} of the provider + * @see HealthResolver + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + */ + public SingleNodeHealthResolver(Map<String, ClusterActor> map, Properties properties, DbLibService dbLib) { + super(map, properties, dbLib); + resolveSites(); + } + + /** + * Implementation of {@code getClusterHealth()}. Uses the + * {@code ShardResolver} to gather health information about the controller. + * This method assumes the cluster is always healthy since it is a single + * node. + * + * @return an {@code ClusterHealth} object with health of the akka cluster + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see ClusterHealth + * @see ShardResolver + */ + @Override + public ClusterHealth getClusterHealth() { + log.info("getClusterHealth(): Getting cluster health..."); + shardResolver.getControllerHealth(memberMap); + return new ClusterHealth().withHealth(Health.HEALTHY); + } + + /** + * Implementation of {@code getSiteHealth()}. Uses the results from + * {@code getAdminHealth}, {@code getDatabaseHealth}, and + * {@code getClusterHealth} to determine the health of the site. If all + * components are healthy, the site is healthy. + * + * @return a List of {@code SiteHealth} objects with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see SiteHealth + * @see ShardResolver + */ + @Override + public List<SiteHealth> getSiteHealth() { + log.info("getSiteHealth(): Getting site health..."); + AdminHealth adminHealth = getAdminHealth(); + DatabaseHealth databaseHealth = getDatabaseHealth(); + ClusterHealth clusterHealth = getClusterHealth(); + SiteHealth siteHealth = new SiteHealth() + .withAdminHealth(adminHealth) + .withDatabaseHealth(databaseHealth) + .withClusterHealth(clusterHealth) + .withRole("ACTIVE") + .withId(getSiteIdentifier()); + log.info("getSiteHealth(): Admin Health: {}", adminHealth.getHealth().toString()); + log.info("getSiteHealth(): Database Health: {}", databaseHealth.getHealth().toString()); + log.info("getSiteHealth(): Cluster Health: {}", clusterHealth.getHealth().toString()); + if(isHealthy(adminHealth.getHealth()) && isHealthy(databaseHealth.getHealth()) && isHealthy(clusterHealth.getHealth())) { + siteHealth.setHealth(Health.HEALTHY); + } + + return Collections.singletonList(siteHealth); + } + + /** + * Implementation of {@code tryFailover()}. No controller-level failover + * options are available in a single node architecture, so 400 Bad Request + * is returned, and no action is taken. + * + * @return an {@code SiteHealth} object with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see FailoverStatus + * @see FailoverInput + */ + @Override + public FailoverStatus tryFailover(FailoverInput input) { + log.info("tryFailover(): Failover not supported in the current configuration."); + return new FailoverStatus().withStatusCode(400).withMessage("Failover not supported in current configuration."); + } + + /** + * Implementation of {@code resolveSites()}. Calls + * {@code resolveSiteForMember()} to resolve which site a member belongs to. + * + * @see HealthResolver + */ + @Override + public void resolveSites() { + log.info("Map contains {} entries", memberMap.size()); + memberMap.forEach((key, value) -> resolveSiteForMember(value)); + } + + /** + * Resolves which site a member belongs to. Since this is a Single node + * architecture, it is defaulted to <i>Site 1</i>. + * + * @see HealthResolver + */ + private void resolveSiteForMember(ClusterActor actor) { + actor.setSite("Site 1"); + log.info("resolveSiteForMember(): {} belongs to {}", actor.getNode(), actor.getSite()); + } +} diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolver.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolver.java new file mode 100644 index 000000000..e79262cf3 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolver.java @@ -0,0 +1,316 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import org.json.JSONArray; +import org.json.JSONObject; + +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionManager; +import org.onap.ccsdk.sli.plugins.grtoolkit.connection.ConnectionResponse; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * Implementation of {@code HealthResolver} for a six node controller + * architecture, where three nodes are located in one data center, and the + * other three nodes are located in another. The sites are assumed to be in an + * Active/Standby configuration, with the Active site nodes voting and the + * Standby site notes non-voting. + * + * @author Anthony Haddox + * @see HealthResolver + */ +public class SixNodeHealthResolver extends HealthResolver { + private final Logger log = LoggerFactory.getLogger(SixNodeHealthResolver.class); + + /** + * Constructs the health resolver used by the {@code GrToolkitProvider} to + * determine the health of the application components. + * + * @param map a HashMap containing all of the nodes in the akka cluster + * @param properties the properties passed ino the provider + * @param dbLib a reference to the {@code DbLibService} of the provider + * @see HealthResolver + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + */ + public SixNodeHealthResolver(Map<String, ClusterActor> map, Properties properties, DbLibService dbLib) { + super(map, properties, dbLib); + resolveSites(); + } + + /** + * Implementation of {@code getClusterHealth()}. Uses the + * {@code ShardResolver} to gather health information about the controller. + * If 4 of 6 members are healthy, the cluster is deemed healthy. + * + * @return an {@code ClusterHealth} object with health of the akka cluster + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see ClusterHealth + * @see ShardResolver + */ + @Override + public ClusterHealth getClusterHealth() { + log.info("getClusterHealth(): Getting cluster health..."); + shardResolver.getControllerHealth(memberMap); + long healthyMembers = memberMap.values().stream().filter(member -> member.isUp() && ! member.isUnreachable()).count(); + return (healthyMembers > 4) ? new ClusterHealth().withHealth(Health.HEALTHY) : new ClusterHealth().withHealth(Health.FAULTY); + } + + /** + * Implementation of {@code getSiteHealth()}. Gathers health information on + * all of the contollers, then separates the nodes into voting and + * non-voting sites. Each site is then checked for its health and the + * result is returned as a List. + * + * @return a List of {@code SiteHealth} objects with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see SiteHealth + * @see ShardResolver + */ + @Override + public List<SiteHealth> getSiteHealth() { + log.info("getSiteHealth(): Getting site health..."); + + // Get cluster health to populate memberMap with necessary values + getClusterHealth(); + List<ClusterActor> votingActors = memberMap.values().stream().filter(ClusterActor::isVoting).collect(Collectors.toList()); + List<ClusterActor> nonVotingActors = memberMap.values().stream().filter(member -> !member.isVoting()).collect(Collectors.toList()); + + SiteHealth votingSiteHealth = getSiteHealth(votingActors).withRole("ACTIVE"); + SiteHealth nonVotingSiteHealth = getSiteHealth(nonVotingActors).withRole("STANDBY"); + return Arrays.asList(votingSiteHealth, nonVotingSiteHealth); + } + + /** + * Gathers the site identifier, admin health, and database health of a + * site. + * + * @return a {@code SiteHealth} object with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see ClusterActor + * @see SiteHealth + * @see ConnectionManager + */ + public SiteHealth getSiteHealth(List<ClusterActor> actorList) { + AdminHealth adminHealth = null; + DatabaseHealth databaseHealth = null; + String siteId = null; + int healthyMembers = 0; + + for(ClusterActor actor : actorList) { + if(actor.isUp() && !actor.isUnreachable()) { + healthyMembers++; + } + if(siteId == null) { + try { + String content = ConnectionManager.getConnectionResponse(httpProtocol + actor.getNode() + ":" + controllerPort + "/restconf/operations/gr-toolkit:site-identifier", ConnectionManager.HttpMethod.POST, null, credentials).content; + siteId = new JSONObject(content).getJSONObject(OUTPUT).getString("id"); + } catch(IOException e) { + log.error("getSiteHealth(): Error getting site identifier from {}", actor.getNode()); + log.error("getSiteHealth(): IOException", e); + } + } + if(adminHealth == null) { + try { + boolean isAdminHealthy = isRemoteComponentHealthy(httpProtocol + actor.getNode() + ":" + controllerPort + "/restconf/operations/gr-toolkit:admin-health"); + if(isAdminHealthy) { + adminHealth = new AdminHealth(Health.HEALTHY, 200); + } + } catch(IOException e) { + log.error("getSiteHealth(): Error getting admin health from {}", actor.getNode()); + log.error("getSiteHealth(): IOException", e); + } + } + if(databaseHealth == null) { + try { + boolean isDatabaseHealthy = isRemoteComponentHealthy(httpProtocol + actor.getNode() + ":" + controllerPort + "/restconf/operations/gr-toolkit:database-health"); + if(isDatabaseHealthy) { + databaseHealth = new DatabaseHealth(Health.HEALTHY); + } + } catch(IOException e) { + log.error("getSiteHealth(): Error getting database health from {}", actor.getNode()); + log.error("getSiteHealth(): IOException", e); + } + } + } + + if(siteId == null) { + siteId = "UNKNOWN SITE"; + } + if(adminHealth == null) { + adminHealth = new AdminHealth(Health.FAULTY, 500); + } + if(databaseHealth == null) { + databaseHealth = new DatabaseHealth(Health.FAULTY); + } + SiteHealth health = new SiteHealth() + .withAdminHealth(adminHealth) + .withDatabaseHealth(databaseHealth) + .withId(siteId); + if(isHealthy(adminHealth.getHealth()) && isHealthy(databaseHealth.getHealth()) && healthyMembers > 1) { + health.setHealth(Health.HEALTHY); + } + + return health; + } + + /** + * Implementation of {@code tryFailover()}. Performs a preliminary call to + * {@code getClusterHealth} to populate information about the cluster. If + * no voting members can be found, the method terminates immediately. The + * nodes are separated into voting and non-voting sites, and a driving + * operator is selected from the non-voting nodes to perform requests + * against. A payload to swap voting between sites is sent to the operator + * to perform a controller-level failover. + * + * @return an {@code SiteHealth} object with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see FailoverStatus + * @see FailoverInput + */ + @Override + public FailoverStatus tryFailover(FailoverInput input) { + // Get Cluster Health to populate the memberMap with the necessary values + log.info("tryFailover(): Performing preliminary health check..."); + getClusterHealth(); + FailoverStatus status = new FailoverStatus(); + ConnectionResponse votingResponse = null; + List<ClusterActor> votingActors = memberMap.values().stream().filter(ClusterActor::isVoting).collect(Collectors.toList()); + List<ClusterActor> nonVotingActors = memberMap.values().stream().filter(member -> !member.isVoting()).collect(Collectors.toList()); + + if(nonVotingActors.size() == 0) { + status.setStatusCode(500); + status.setMessage("No nonvoting members found. Cannot perform voting switch."); + return status; + } + + ClusterActor operator; + try { + operator = nonVotingActors.stream().filter(this::isControllerHealthy).findFirst().get(); + } catch(NoSuchElementException e) { + log.error("tryFailover(): Could not find any healthy members.", e); + status.setStatusCode(500); + status.setMessage("Could not find any healthy members."); + return status; + } + + // Assuming two 3 node sites, 3 voting and 3 non voting + if(votingActors.size() < 3 || nonVotingActors.size() < 3) { + log.warn("tryFailover(): Sites do not contain an equal amount of voting and nonvoting members: Voting: {} | NonVoting: {}", votingActors.size(), nonVotingActors.size()); + } + log.info("tryFailover(): Swapping voting..."); + try { + JSONObject votingInput = new JSONObject(); + JSONObject inputBlock = new JSONObject(); + JSONArray votingStateArray = new JSONArray(); + JSONObject memberVotingState; + for(ClusterActor actor : votingActors) { + memberVotingState = new JSONObject(); + memberVotingState.put("member-name", actor.getMember()); + memberVotingState.put("voting", false); + votingStateArray.put(memberVotingState); + } + for(ClusterActor actor : nonVotingActors) { + memberVotingState = new JSONObject(); + memberVotingState.put("member-name", actor.getMember()); + memberVotingState.put("voting", true); + votingStateArray.put(memberVotingState); + } + inputBlock.put("member-voting-state", votingStateArray); + votingInput.put("input", inputBlock); + log.debug("tryFailover(): {}", votingInput); + // Change voting all shards + votingResponse = ConnectionManager.getConnectionResponse(httpProtocol + operator.getNode() + ":" + controllerPort + "/restconf/operations/cluster-admin:change-member-voting-states-for-all-shards", ConnectionManager.HttpMethod.POST, votingInput.toString(), credentials); + } catch(IOException e) { + log.error("tryFailover(): Failure changing voting", e); + } + if(votingResponse != null) { + if(votingResponse.statusCode != 200) { + status.setStatusCode(votingResponse.statusCode); + status.setMessage("Failed to swap voting."); + } else { + status.setStatusCode(200); + status.setMessage("Failover complete."); + } + } else { + status.setStatusCode(500); + status.setMessage("Failed to swap voting."); + } + + return status; + } + + /** + * Implementation of {@code resolveSites()}. Calls + * {@code resolveSiteForMember()} to resolve which site a member belongs to. + * + * @see HealthResolver + */ + @Override + public void resolveSites() { + log.info("Map contains {} entries", memberMap.size()); + memberMap.forEach((key, value) -> resolveSiteForMember(value)); + } + + /** + * Resolves which site a member belongs to. Members 1-3 are assumed to be + * <i>Site 1</i> while members 4-6 are assumed to be <i>Site 2</i>. + * + * @see HealthResolver + */ + private void resolveSiteForMember(ClusterActor actor) { + try { + int memberNumber = Integer.parseInt(actor.getMember().split("-")[1]); + if(memberNumber < 4) { + actor.setSite("Site 1"); + } else { + actor.setSite("Site 2"); + } + log.info("resolveSiteForMember(): {} belongs to {}", actor.getNode(), actor.getSite()); + } catch (NumberFormatException e) { + log.error("resolveSiteForMember(): Could not parse member number for {}. Defaulting to Site 1.", actor.getNode()); + actor.setSite("resolveSiteForMember(): Site 1"); + } + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolver.java b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolver.java new file mode 100644 index 000000000..2180b2bf8 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolver.java @@ -0,0 +1,162 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverInput; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Implementation of {@code HealthResolver} for a three node controller + * architecture, where all nodes are located within the same data center or + * geographic region. The nodes are assumed to be in an Active/Active/Active + * voting configuration. + * + * @author Anthony Haddox + * @see HealthResolver + */ +public class ThreeNodeHealthResolver extends HealthResolver { + private final Logger log = LoggerFactory.getLogger(ThreeNodeHealthResolver.class); + + /** + * Constructs the health resolver used by the {@code GrToolkitProvider} to + * determine the health of the application components. + * + * @param map a HashMap containing all of the nodes in the akka cluster + * @param properties the properties passed ino the provider + * @param dbLib a reference to the {@code DbLibService} of the provider + * @see HealthResolver + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + */ + public ThreeNodeHealthResolver(Map<String, ClusterActor> map, Properties properties, DbLibService dbLib) { + super(map, properties, dbLib); + resolveSites(); + } + + /** + * Implementation of {@code getClusterHealth()}. Uses the + * {@code ShardResolver} to gather health information about the controller. + * If 2 of 3 members are healthy, the cluster is deemed healthy. + * + * @return an {@code ClusterHealth} object with health of the akka cluster + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see ClusterHealth + * @see ShardResolver + */ + @Override + public ClusterHealth getClusterHealth() { + log.info("getClusterHealth(): Getting cluster health..."); + shardResolver.getControllerHealth(memberMap); + long healthyMembers = memberMap.values().stream().filter(member -> member.isUp() && ! member.isUnreachable()).count(); + return (healthyMembers > 1) ? new ClusterHealth().withHealth(Health.HEALTHY) : new ClusterHealth().withHealth(Health.FAULTY); + } + + /** + * Implementation of {@code getSiteHealth()}. Uses the results from + * {@code getAdminHealth}, {@code getDatabaseHealth}, and + * {@code getClusterHealth} to determine the health of the site. If all + * components are healthy, the site is healthy. + * + * @return a List of {@code SiteHealth} objects with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see SiteHealth + * @see ShardResolver + */ + @Override + public List<SiteHealth> getSiteHealth() { + log.info("getSiteHealth(): Getting site health..."); + AdminHealth adminHealth = getAdminHealth(); + DatabaseHealth databaseHealth = getDatabaseHealth(); + ClusterHealth clusterHealth = getClusterHealth(); + SiteHealth siteHealth = new SiteHealth() + .withAdminHealth(adminHealth) + .withDatabaseHealth(databaseHealth) + .withClusterHealth(clusterHealth) + .withRole("ACTIVE") + .withId(getSiteIdentifier()); + log.info("getSiteHealth(): Admin Health: {}", adminHealth.getHealth().toString()); + log.info("getSiteHealth(): Database Health: {}", databaseHealth.getHealth().toString()); + log.info("getSiteHealth(): Cluster Health: {}", clusterHealth.getHealth().toString()); + if(isHealthy(adminHealth.getHealth()) && isHealthy(databaseHealth.getHealth()) && isHealthy(clusterHealth.getHealth())) { + siteHealth.setHealth(Health.HEALTHY); + } + + return Collections.singletonList(siteHealth); + } + + /** + * Implementation of {@code tryFailover()}. No controller-level failover + * options are available in a three node architecture, so 400 Bad Request + * is returned, and no action is taken. + * + * @return an {@code SiteHealth} object with health of the site + * @see org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider + * @see HealthResolver + * @see FailoverStatus + * @see FailoverInput + */ + @Override + public FailoverStatus tryFailover(FailoverInput input) { + log.info("tryFailover(): Failover not supported in the current configuration."); + return new FailoverStatus().withStatusCode(400).withMessage("Failover not supported in current configuration."); + } + + /** + * Implementation of {@code resolveSites()}. Calls + * {@code resolveSiteForMember()} to resolve which site a member belongs to. + * + * @see HealthResolver + */ + @Override + public void resolveSites() { + log.info("Map contains {} entries", memberMap.size()); + memberMap.forEach((key, value) -> resolveSiteForMember(value)); + } + + /** + * Resolves which site a member belongs to. Since this is a three node + * co-located architecture, it is defaulted to <i>Site 1</i>. + * + * @see HealthResolver + */ + private void resolveSiteForMember(ClusterActor actor) { + actor.setSite("Site 1"); + log.info("resolveSiteForMember(): {} belongs to {}", actor.getNode(), actor.getSite()); + } +} diff --git a/plugins/grToolkit/provider/src/main/resources/gr-toolkit.properties b/plugins/grToolkit/provider/src/main/resources/gr-toolkit.properties new file mode 100755 index 000000000..e3463df08 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/resources/gr-toolkit.properties @@ -0,0 +1,35 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +resolver=org.onap.ccsdk.sli.plugins.grtoolkit.resolver.SingleNodeHealthResolver +akka.conf.location=/opt/opendaylight/current/controller/configuration/initial/akka.conf +adm.useSsl=true +adm.fqdn= +adm.healthcheck=/healthcheck +adm.port.http=8181 +adm.port.ssl=8443 +controller.credentials=admin:admin +controller.useSsl=true +controller.port.http=8181 +controller.port.ssl=8443 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +site.identifier=UniqueSiteNameHere diff --git a/plugins/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml b/plugins/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml new file mode 100644 index 000000000..5a4492c56 --- /dev/null +++ b/plugins/grToolkit/provider/src/main/resources/org/opendaylight/blueprint/GrToolkit.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <reference id="dataBroker" + interface="org.opendaylight.controller.md.sal.binding.api.DataBroker" + odl:type="default" /> + + <reference id="notificationService" + interface="org.opendaylight.controller.md.sal.binding.api.NotificationPublishService" + odl:type="default" /> + + <reference id="rpcRegistry" + interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry" + odl:type="default" /> + + <reference id="dbLib" + interface="org.onap.ccsdk.sli.core.dblib.DbLibService" /> + + <reference id="configDatastore" interface="org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface" + odl:type="distributed-config"/> + + <bean id="provider" class="org.onap.ccsdk.sli.plugins.grtoolkit.GrToolkitProvider"> + <argument ref="dataBroker" /> + <argument ref="notificationService" /> + <argument ref="rpcRegistry" /> + <argument ref="dbLib" /> + <argument ref="configDatastore" /> + </bean> + + <odl:rpc-implementation ref="provider"/> +</blueprint> diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java new file mode 100644 index 000000000..edd6d1b8d --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/GrToolkitProviderTest.java @@ -0,0 +1,352 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit; +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import com.google.common.util.concurrent.ListenableFuture; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; + +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; + +import org.opendaylight.controller.cluster.access.concepts.MemberName; +import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface; +import org.opendaylight.controller.cluster.datastore.utils.ActorUtils; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.AdminHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ClusterHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.DatabaseHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.FailoverOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.HaltAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficInputBuilder; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.ResumeAkkaTrafficOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteHealthOutput; +import org.opendaylight.yang.gen.v1.org.onap.ccsdk.sli.plugins.gr.toolkit.rev180926.SiteIdentifierOutput; +import org.opendaylight.yangtools.yang.common.RpcResult; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class GrToolkitProviderTest { + GrToolkitProvider provider; + GrToolkitProvider providerSpy; + DataBroker dataBroker; + NotificationPublishService notificationProviderService; + RpcProviderRegistry rpcProviderRegistry; + DistributedDataStoreInterface configDatastore; + DbLibService dbLibService; + DBLibConnection connection; + Properties properties; + + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + @Rule + public WireMockRule wireMockRule = new WireMockRule(9999); + + @Before + public void setup() { + environmentVariables.set("SDNC_CONFIG_DIR","src/test/resources"); + dataBroker = mock(DataBroker.class); + notificationProviderService = mock(NotificationPublishService.class); + rpcProviderRegistry = mock(RpcProviderRegistry.class); + configDatastore = mock(DistributedDataStoreInterface.class); + dbLibService = mock(DbLibService.class); + connection = mock(DBLibConnection.class); + + ActorUtils actorContext = mock(ActorUtils.class); + MemberName memberName = MemberName.forName("Test"); + + when(actorContext.getCurrentMemberName()).thenReturn(memberName); + when(configDatastore.getActorUtils()).thenReturn(actorContext); + + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + + provider = new GrToolkitProvider(dataBroker, notificationProviderService, + rpcProviderRegistry, configDatastore, dbLibService); + providerSpy = spy(provider); + stubController(); + } + + @Test + public void closeTest() { + try { + provider.close(); + } catch(Exception e) { + // Exception expected + } + } + + @Test + public void onDataTreeChangedTest() { + provider.onDataTreeChanged(new ArrayList()); + // onDataTreeChanged is an empty stub + } + + private void stubController() { + String clusterBody = null; + String shardManagerBody = null; + String shardDefaultBody = null; + String shardOperationalBody = null; + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/cluster.json"))) { + clusterBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/shard-manager.json"))) { + shardManagerBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/default-config.json"))) { + shardDefaultBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/default-operational.json"))) { + shardOperationalBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + + if(clusterBody == null || shardManagerBody == null || shardDefaultBody == null || shardOperationalBody == null) { + fail(); + } + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBody(clusterBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardManagerBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardDefaultBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore")).willReturn(aResponse().withStatus(200).withBody(shardOperationalBody))); + } + + @Test + public void clusterHealthTest() { + ListenableFuture<RpcResult<ClusterHealthOutput>> result = provider.clusterHealth(null); + try { + assertEquals("0", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void siteHealthTest() { + ListenableFuture<RpcResult<SiteHealthOutput>> result = provider.siteHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthTest() { + ListenableFuture<RpcResult<DatabaseHealthOutput>> result = provider.databaseHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthWhenROTest() { + try { + when(connection.isReadOnly()).thenReturn(true); + } catch(SQLException e) { + fail(); + } + ListenableFuture<RpcResult<DatabaseHealthOutput>> result = provider.databaseHealth(null); + try { + assertEquals("500", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void databaseHealthWhenExceptionTest() { + try { + when(connection.isReadOnly()).thenThrow(new SQLException()); + } catch(SQLException e) { + //expected + } + ListenableFuture<RpcResult<DatabaseHealthOutput>> result = provider.databaseHealth(null); + try { + assertEquals("500", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void adminHealthTest() { + ListenableFuture<RpcResult<AdminHealthOutput>> result = provider.adminHealth(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void siteIdentifierTest() { + ListenableFuture<RpcResult<SiteIdentifierOutput>> result = provider.siteIdentifier(null); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void failoverTest() { + ListenableFuture<RpcResult<FailoverOutput>> result = provider.failover(null); + try { + assertEquals("400", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void haltTrafficTest() { + HaltAkkaTrafficInputBuilder builder = new HaltAkkaTrafficInputBuilder(); + builder.setNodeInfo(new ArrayList<>()); + ListenableFuture<RpcResult<HaltAkkaTrafficOutput>> result = provider.haltAkkaTraffic(builder.build()); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void resumeTrafficTest() { + ResumeAkkaTrafficInputBuilder builder = new ResumeAkkaTrafficInputBuilder(); + builder.setNodeInfo(new ArrayList<>()); + ListenableFuture<RpcResult<ResumeAkkaTrafficOutput>> result = provider.resumeAkkaTraffic(builder.build()); + try { + assertEquals("200", result.get().getResult().getStatus()); + } catch(InterruptedException | ExecutionException e) { + fail(); + } + } + + @Test + public void executeCommandTest() { + try { + Method method = provider.getClass().getDeclaredMethod("executeCommand", String.class); + method.setAccessible(true); + method.invoke(provider, "ls"); + } catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void isolateSiteFromClusterTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-node"); + actor.setAkkaPort("2550"); + ArrayList<ClusterActor> activeList = new ArrayList<>(); + activeList.add(actor); + ArrayList<ClusterActor> standbyList = new ArrayList<>(); + standbyList.add(actor); + Method method = provider.getClass().getDeclaredMethod("isolateSiteFromCluster", ArrayList.class, ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, standbyList, "80"); + } catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void downUnreachableNodesTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-node"); + actor.setAkkaPort("2550"); + ArrayList<ClusterActor> activeList = new ArrayList<>(); + activeList.add(actor); + ArrayList<ClusterActor> standbyList = new ArrayList<>(); + standbyList.add(actor); + Method method = provider.getClass().getDeclaredMethod("downUnreachableNodes", ArrayList.class, ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, standbyList, "80"); + } catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void backupMdSalTest() { + try { + ClusterActor actor = new ClusterActor(); + actor.setNode("some-Node"); + actor.setAkkaPort("2550"); + ArrayList<ClusterActor> activeList = new ArrayList<>(); + activeList.add(actor); + Method method = provider.getClass().getDeclaredMethod("backupMdSal", ArrayList.class, String.class); + method.setAccessible(true); + method.invoke(provider, activeList, "80"); + } catch(NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail(); + } + } +} diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManagerTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManagerTest.java new file mode 100644 index 000000000..f749688d9 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionManagerTest.java @@ -0,0 +1,65 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.connection; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Rule; +import org.junit.Test; + +import static org.junit.Assert.*; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; + +public class ConnectionManagerTest { + @Rule + public WireMockRule wireMockRule = new WireMockRule(9999); + + @Test + public void getConnectionResponseWithInput() throws Exception { + stubFor(post(urlEqualTo("/post")) + .willReturn(aResponse().withStatus(200))); + ConnectionResponse response = ConnectionManager.getConnectionResponse("http://localhost:9999/post", ConnectionManager.HttpMethod.POST, "", "creds:creds"); + assertNotNull(response); + assertEquals(200, response.statusCode); + } + + @Test + public void getConnectionResponseWithCredentials() throws Exception { + stubFor(post(urlEqualTo("/post")) + .willReturn(aResponse().withStatus(200))); + ConnectionResponse response = ConnectionManager.getConnectionResponse("http://localhost:9999/post", ConnectionManager.HttpMethod.POST, "", "creds:creds"); + assertNotNull(response); + assertEquals(200, response.statusCode); + } + + @Test + public void getConnectionResponse() throws Exception { + stubFor(get(urlEqualTo("/get")) + .willReturn(aResponse().withStatus(200) + .withBody("Multi\nLine\nResponse"))); + ConnectionResponse response = ConnectionManager.getConnectionResponse("http://localhost:9999/get", ConnectionManager.HttpMethod.GET, null, null); + assertNotNull(response); + assertEquals(200, response.statusCode); + assertEquals("MultiLineResponse", response.content); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponseTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponseTest.java new file mode 100644 index 000000000..a9f0edc30 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/connection/ConnectionResponseTest.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.connection; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ConnectionResponseTest { + @Test + public void constructorTest() { + ConnectionResponse response = new ConnectionResponse(); + assertNotNull(response); + assertEquals(0, response.statusCode); + assertNull(response.content); + assertTrue(response.toString().length() > 0); + } + @Test + public void withStatusCode() { + ConnectionResponse response = new ConnectionResponse().withStatusCode(123); + assertEquals(123, response.statusCode); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealthTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealthTest.java new file mode 100644 index 000000000..fa56a4d49 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/AdminHealthTest.java @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class AdminHealthTest { + @Test + public void constructorTest() { + AdminHealth health = new AdminHealth(Health.HEALTHY); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void constructor2Test() { + AdminHealth health = new AdminHealth(Health.FAULTY, 500); + assertEquals(Health.FAULTY, health.getHealth()); + assertEquals(500, health.getStatusCode()); + } + + @Test + public void setHealth() { + AdminHealth health = new AdminHealth(Health.HEALTHY, 201); + assertEquals(Health.HEALTHY, health.getHealth()); + assertEquals(201, health.getStatusCode()); + health.setHealth(Health.FAULTY); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void setStatusCode() { + AdminHealth health = new AdminHealth(Health.HEALTHY, 200); + assertEquals(Health.HEALTHY, health.getHealth()); + assertEquals(200, health.getStatusCode()); + health.setStatusCode(409); + assertEquals(409, health.getStatusCode()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealthTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealthTest.java new file mode 100644 index 000000000..2e2ab3fd7 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/ClusterHealthTest.java @@ -0,0 +1,47 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ClusterHealthTest { + @Test + public void constructorTest() { + ClusterHealth health = new ClusterHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void withHealth() { + ClusterHealth health = new ClusterHealth().withHealth(Health.HEALTHY); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void setHealth() { + ClusterHealth health = new ClusterHealth(); + health.setHealth(Health.HEALTHY); + assertEquals(Health.HEALTHY, health.getHealth()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealthTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealthTest.java new file mode 100644 index 000000000..05621a503 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/DatabaseHealthTest.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class DatabaseHealthTest { + @Test + public void constructorTest() { + DatabaseHealth health = new DatabaseHealth(Health.FAULTY); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void setHealth() { + DatabaseHealth health = new DatabaseHealth(Health.FAULTY); + assertEquals(Health.FAULTY, health.getHealth()); + health.setHealth(Health.HEALTHY); + assertEquals(Health.HEALTHY, health.getHealth()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatusTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatusTest.java new file mode 100644 index 000000000..b5b3d00d2 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/FailoverStatusTest.java @@ -0,0 +1,60 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class FailoverStatusTest { + @Test + public void constructorTest() { + FailoverStatus status = new FailoverStatus(); + assertEquals(200, status.getStatusCode()); + assertEquals("Failover complete.", status.getMessage()); + } + @Test + public void withStatusCode() { + FailoverStatus status = new FailoverStatus().withStatusCode(500); + assertEquals(500, status.getStatusCode()); + } + + @Test + public void withMessage() { + FailoverStatus status = new FailoverStatus().withMessage("Test"); + assertEquals("Test", status.getMessage()); + } + + @Test + public void setStatusCode() { + FailoverStatus status = new FailoverStatus(); + status.setStatusCode(500); + assertEquals(500, status.getStatusCode()); + } + + @Test + public void setMessage() { + FailoverStatus status = new FailoverStatus(); + status.setMessage("Test"); + assertEquals("Test", status.getMessage()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/HealthTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/HealthTest.java new file mode 100644 index 000000000..1115d2084 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/HealthTest.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class HealthTest { + @Test + public void getHealth() { + assertEquals("HEALTHY", Health.HEALTHY.getHealth()); + assertEquals("FAULTY", Health.FAULTY.getHealth()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java new file mode 100644 index 000000000..7ed0135a6 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/MemberBuilderTest.java @@ -0,0 +1,62 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; + +import static org.junit.Assert.*; + +public class MemberBuilderTest { + MemberBuilder builder; + ClusterActor actor; + + @Before + public void setUp() { + actor = new ClusterActor(); + actor.setUp(true); + actor.setVoting(true); + } + + @Test + public void constructorTest() { + ArrayList<String> actorList = new ArrayList<>(); + ArrayList<String> shardList = new ArrayList<>(); + HashMap<String, Integer> commitMap = new HashMap<>(); + actorList.add("Some-Actor"); + shardList.add("Some-shard"); + commitMap.put("Some-shard", 4); + commitMap.put("Some-other-shard", -4); + actor.setShardLeader(actorList); + actor.setReplicaShards(shardList); + actor.setNonReplicaShards(shardList); + actor.setCommits(commitMap); + assertNotNull(actor.toString()); + assertEquals("", actor.getSite()); + assertEquals(1, actor.getNonReplicaShards().size()); + builder = new MemberBuilder(actor); + assertNotNull(builder.build()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealthTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealthTest.java new file mode 100644 index 000000000..7b74991e7 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/data/SiteHealthTest.java @@ -0,0 +1,108 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.data; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SiteHealthTest { + @Test + public void constructorTest() { + SiteHealth health = new SiteHealth(); + assertNotNull(health.getAdminHealth()); + assertNotNull(health.getDatabaseHealth()); + assertNotNull(health.getClusterHealth()); + assertEquals(Health.FAULTY, health.getHealth()); + } + @Test + public void withAdminHealth() { + SiteHealth health = new SiteHealth().withAdminHealth(new AdminHealth(Health.HEALTHY)); + assertEquals(Health.HEALTHY, health.getAdminHealth().get(0).getHealth()); + } + + @Test + public void withDatabaseHealth() { + SiteHealth health = new SiteHealth().withDatabaseHealth(new DatabaseHealth(Health.HEALTHY)); + assertEquals(Health.HEALTHY, health.getDatabaseHealth().get(0).getHealth()); + } + + @Test + public void withClusterHealth() { + SiteHealth health = new SiteHealth().withClusterHealth(new ClusterHealth()); + assertEquals(Health.FAULTY, health.getClusterHealth().get(0).getHealth()); + } + + @Test + public void withId() { + SiteHealth health = new SiteHealth().withId("My_ID"); + assertEquals("My_ID", health.getId()); + } + + @Test + public void withRole() { + SiteHealth health = new SiteHealth().withRole("My_role"); + assertEquals("My_role", health.getRole()); + } + + @Test + public void setHealth() { + SiteHealth health = new SiteHealth(); + health.setHealth(Health.HEALTHY); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void setAdminHealth() { + SiteHealth health = new SiteHealth().withAdminHealth(new AdminHealth(Health.HEALTHY)); + health.setAdminHealth(null); + assertNull(health.getAdminHealth()); + } + + @Test + public void setDatabaseHealth() { + SiteHealth health = new SiteHealth().withDatabaseHealth(new DatabaseHealth(Health.HEALTHY)); + health.setDatabaseHealth(null); + assertNull(health.getDatabaseHealth()); + } + + @Test + public void setClusterHealth() { + SiteHealth health = new SiteHealth().withClusterHealth(new ClusterHealth()); + health.setClusterHealth(null); + assertNull(health.getClusterHealth()); + } + + @Test + public void setId() { + SiteHealth health = new SiteHealth().withId("My_ID"); + health.setId("My_new_ID"); + assertEquals("My_new_ID", health.getId()); + } + + @Test + public void setRole() { + SiteHealth health = new SiteHealth().withRole("My_role"); + health.setRole("My_new_role"); + assertEquals("My_new_role", health.getRole()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolverTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolverTest.java new file mode 100644 index 000000000..2827b4055 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SingleNodeHealthResolverTest.java @@ -0,0 +1,235 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SingleNodeHealthResolverTest { + private Map<String, ClusterActor> memberMap; + private DbLibService dbLibService; + private DBLibConnection connection; + private SingleNodeHealthResolver resolver; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(9999); + + @Before + public void setUp() { + memberMap = generateMemberMap(1); + Properties properties = new Properties(); + try(FileInputStream fileInputStream = new FileInputStream("src/test/resources/single/gr-toolkit.properties")) { + properties.load(fileInputStream); + } catch(IOException e) { + fail(); + } + + dbLibService = mock(DbLibService.class); + connection = mock(DBLibConnection.class); + resolver = new SingleNodeHealthResolver(memberMap, properties, dbLibService); + } + + private Map<String, ClusterActor> generateMemberMap(int memberCount) { + Map<String, ClusterActor> map = new HashMap<>(); + ClusterActor actor; + for(int ndx = 0; ndx < memberCount; ndx++) { + actor = new ClusterActor(); + actor.setNode("localhost"); + actor.setAkkaPort("2550"); + actor.setMember("member-" + (ndx + 1)); + actor.setUp(true); + actor.setUnreachable(false); + + map.put(actor.getNode(), actor); + } + return map; + } + + @Test + public void getAdminHealthFaulty() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(500))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(500, health.getStatusCode()); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getAdminHealthHealthy() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(200, health.getStatusCode()); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealth() { + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealthFaulty() { + try { + when(connection.isReadOnly()).thenReturn(true); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getDatabaseHealthException() { + try { + when(connection.isReadOnly()).thenThrow(new SQLException()); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void siteIdentifier() { + assertEquals("TestODL", resolver.getSiteIdentifier()); + resolver.setSiteIdentifier("NewTestODL"); + assertEquals("NewTestODL", resolver.getSiteIdentifier()); + } + + @Test + public void getClusterHealth() { + stubController(); + ClusterHealth health = resolver.getClusterHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + private void stubController() { + String clusterBody = null; + String shardManagerBody = null; + String shardDefaultBody = null; + String shardOperationalBody = null; + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/single/cluster.json"))) { + clusterBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/single/shard-manager.json"))) { + shardManagerBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/single/default-config.json"))) { + shardDefaultBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/single/default-operational.json"))) { + shardOperationalBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + + if(clusterBody == null || shardManagerBody == null || shardDefaultBody == null || shardOperationalBody == null) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBody(clusterBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).willReturn(aResponse().withStatus(200).withBody(shardManagerBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).willReturn(aResponse().withStatus(200).withBody(shardDefaultBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore")).willReturn(aResponse().withStatus(200).withBody(shardOperationalBody))); + } + + @Test + public void getSiteHealth() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.HEALTHY, health.get(0).getHealth()); + } + + @Test + public void tryFailover() { + FailoverStatus status = resolver.tryFailover(null); + assertEquals(400, status.getStatusCode()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolverTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolverTest.java new file mode 100644 index 000000000..cbab450e1 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/SixNodeHealthResolverTest.java @@ -0,0 +1,335 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SixNodeHealthResolverTest { + private Map<String, ClusterActor> memberMap; + private DbLibService dbLibService; + private DBLibConnection connection; + private SixNodeHealthResolver resolver; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(9999); + + @Before + public void setUp() { + memberMap = generateMemberMap(6); + Properties properties = new Properties(); + try(FileInputStream fileInputStream = new FileInputStream("src/test/resources/six/gr-toolkit.properties")) { + properties.load(fileInputStream); + } catch(IOException e) { + fail(); + } + + dbLibService = mock(DbLibService.class); + connection = mock(DBLibConnection.class); + resolver = new SixNodeHealthResolver(memberMap, properties, dbLibService); + } + + private Map<String, ClusterActor> generateMemberMap(int memberCount) { + Map<String, ClusterActor> map = new HashMap<>(); + ClusterActor actor; + for(int ndx = 0; ndx < memberCount; ndx++) { + actor = new ClusterActor(); + actor.setNode("127.0.1." + (ndx + 1)); + actor.setAkkaPort("2550"); + actor.setMember("member-" + (ndx + 1)); + actor.setUp(true); + actor.setUnreachable(false); + + map.put(actor.getNode(), actor); + } + return map; + } + + @Test + public void getAdminHealthFaulty() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(500))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(500, health.getStatusCode()); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getAdminHealthHealthy() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(200, health.getStatusCode()); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealth() { + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealthFaulty() { + try { + when(connection.isReadOnly()).thenReturn(true); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getDatabaseHealthException() { + try { + when(connection.isReadOnly()).thenThrow(new SQLException()); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void siteIdentifier() { + assertEquals("TestODL", resolver.getSiteIdentifier()); + resolver.setSiteIdentifier("NewTestODL"); + assertEquals("NewTestODL", resolver.getSiteIdentifier()); + } + + @Test + public void getClusterHealth() { + stubController(); + ClusterHealth health = resolver.getClusterHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + private void stubController() { + String clusterBody = null; + String shardManagerBody = null; + String shardDefaultBody = null; + String shardOperationalBody = null; + String componentBody = null; + String identifierBody = null; + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/cluster.json"))) { + clusterBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/shard-manager.json"))) { + shardManagerBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/default-config.json"))) { + shardDefaultBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/default-operational.json"))) { + shardOperationalBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/component-health.json"))) { + componentBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/six/site-identifier.json"))) { + identifierBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + + if(clusterBody == null || shardManagerBody == null || shardDefaultBody == null || shardOperationalBody == null + || componentBody == null || identifierBody == null) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBody(clusterBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardManagerBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardDefaultBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore")).willReturn(aResponse().withStatus(200).withBody(shardOperationalBody))); + stubFor(post(urlEqualTo("/restconf/operations/gr-toolkit:site-identifier")).willReturn(aResponse().withStatus(200).withBody(identifierBody))); + stubFor(post(urlEqualTo("/restconf/operations/gr-toolkit:admin-health")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(componentBody)).willSetStateTo("next")); + stubFor(post(urlEqualTo("/restconf/operations/gr-toolkit:database-health")).willReturn(aResponse().withStatus(200).withBody(componentBody))); + } + + @Test + public void getSiteHealth() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(2, health.size()); + assertEquals(Health.HEALTHY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaulty() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).inScenario("testing").whenScenarioStateIs("next").willReturn(aResponse().withBodyFile("nonexistent"))); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(2, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyShard() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withBodyFile("nonexistent")).willSetStateTo("next")); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(2, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyCluster() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBodyFile("nonexistent"))); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(2, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyAdmin() { + stubController(); + stubFor(post(urlEqualTo("/restconf/operations/gr-toolkit:admin-health")).inScenario("testing").willReturn(aResponse().withBodyFile("nonexistent")).willSetStateTo("next")); + stubFor(get(urlEqualTo("/restconf/operations/gr-toolkit:admin-health")).inScenario("testing").whenScenarioStateIs("next").willReturn(aResponse().withBodyFile("nonexistent"))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(2, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + assertEquals(Health.FAULTY, health.get(1).getHealth()); + } + + @Test + public void tryFailover() { + stubController(); + stubFor(get(urlEqualTo("/restconf/operations/cluster-admin:change-member-voting-states-for-all-shards")).willReturn(aResponse().withStatus(200))); + FailoverStatus status = resolver.tryFailover(null); + assertEquals(500, status.getStatusCode()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolverTest.java b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolverTest.java new file mode 100644 index 000000000..4ea07be43 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/java/org/onap/ccsdk/sli/plugins/grtoolkit/resolver/ThreeNodeHealthResolverTest.java @@ -0,0 +1,314 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.grtoolkit.resolver; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import org.onap.ccsdk.sli.core.dblib.DBLibConnection; +import org.onap.ccsdk.sli.core.dblib.DbLibService; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.AdminHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterActor; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.ClusterHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.DatabaseHealth; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.FailoverStatus; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.Health; +import org.onap.ccsdk.sli.plugins.grtoolkit.data.SiteHealth; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ThreeNodeHealthResolverTest { + private Map<String, ClusterActor> memberMap; + private DbLibService dbLibService; + private DBLibConnection connection; + private ThreeNodeHealthResolver resolver; + + @Rule + public WireMockRule wireMockRule = new WireMockRule(9999); + + @Before + public void setUp() { + memberMap = generateMemberMap(3); + Properties properties = new Properties(); + try(FileInputStream fileInputStream = new FileInputStream("src/test/resources/three/gr-toolkit.properties")) { + properties.load(fileInputStream); + } catch(IOException e) { + fail(); + } + + dbLibService = mock(DbLibService.class); + connection = mock(DBLibConnection.class); + resolver = new ThreeNodeHealthResolver(memberMap, properties, dbLibService); + } + + private Map<String, ClusterActor> generateMemberMap(int memberCount) { + Map<String, ClusterActor> map = new HashMap<>(); + ClusterActor actor; + for(int ndx = 0; ndx < memberCount; ndx++) { + actor = new ClusterActor(); + actor.setNode("127.0.1." + (ndx + 1)); + actor.setAkkaPort("2550"); + actor.setMember("member-" + (ndx + 1)); + actor.setUp(true); + actor.setUnreachable(false); + + map.put(actor.getNode(), actor); + } + return map; + } + + @Test + public void getAdminHealthFaulty() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(500))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(500, health.getStatusCode()); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getAdminHealthHealthy() { + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + AdminHealth health = resolver.getAdminHealth(); + assertNotNull(health); + assertEquals(200, health.getStatusCode()); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealth() { + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + @Test + public void getDatabaseHealthFaulty() { + try { + when(connection.isReadOnly()).thenReturn(true); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void getDatabaseHealthException() { + try { + when(connection.isReadOnly()).thenThrow(new SQLException()); + when(connection.isClosed()).thenReturn(true); + when(dbLibService.isActive()).thenReturn(false); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + DatabaseHealth health = resolver.getDatabaseHealth(); + assertEquals(Health.FAULTY, health.getHealth()); + } + + @Test + public void siteIdentifier() { + assertEquals("TestODL", resolver.getSiteIdentifier()); + resolver.setSiteIdentifier("NewTestODL"); + assertEquals("NewTestODL", resolver.getSiteIdentifier()); + } + + @Test + public void getClusterHealth() { + stubController(); + ClusterHealth health = resolver.getClusterHealth(); + assertEquals(Health.HEALTHY, health.getHealth()); + } + + private void stubController() { + String clusterBody = null; + String shardManagerBody = null; + String shardDefaultBody = null; + String shardOperationalBody = null; + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/cluster.json"))) { + clusterBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/shard-manager.json"))) { + shardManagerBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/default-config.json"))) { + shardDefaultBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + try(Stream<String> stream = Files.lines(Paths.get("src/test/resources/three/default-operational.json"))) { + shardOperationalBody = stream.collect(Collectors.joining()); + } catch(IOException e) { + fail(); + } + + if(clusterBody == null || shardManagerBody == null || shardDefaultBody == null || shardOperationalBody == null) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBody(clusterBody))); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardManagerBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withStatus(200).withBody(shardDefaultBody)).willSetStateTo("next")); + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore")).willReturn(aResponse().withStatus(200).withBody(shardOperationalBody))); + } + + @Test + public void getSiteHealth() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.HEALTHY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaulty() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore")).inScenario("testing").whenScenarioStateIs("next").willReturn(aResponse().withBodyFile("nonexistent"))); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyShard() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore")).inScenario("testing").willReturn(aResponse().withBodyFile("nonexistent")).willSetStateTo("next")); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.HEALTHY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyCluster() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(200))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + stubFor(get(urlEqualTo("/jolokia/read/akka:type=Cluster")).willReturn(aResponse().withStatus(200).withBodyFile("nonexistent"))); + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void getSiteHealthFaultyAdmin() { + stubController(); + stubFor(get(urlEqualTo("/adm/healthcheck")).willReturn(aResponse().withStatus(400))); + try { + when(connection.isReadOnly()).thenReturn(false); + when(connection.isClosed()).thenReturn(false); + when(dbLibService.isActive()).thenReturn(true); + when(dbLibService.getConnection()).thenReturn(connection); + } catch(SQLException e) { + fail(); + } + List<SiteHealth> health = resolver.getSiteHealth(); + assertNotNull(health); + assertNotEquals(0, health.size()); + assertEquals(1, health.size()); + assertEquals(Health.FAULTY, health.get(0).getHealth()); + } + + @Test + public void tryFailover() { + FailoverStatus status = resolver.tryFailover(null); + assertEquals(400, status.getStatusCode()); + } +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/akka.conf b/plugins/grToolkit/provider/src/test/resources/akka.conf new file mode 100644 index 000000000..ce46748ec --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/akka.conf @@ -0,0 +1,67 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.1.1:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/gr-toolkit.properties b/plugins/grToolkit/provider/src/test/resources/gr-toolkit.properties new file mode 100755 index 000000000..52b19bf51 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/gr-toolkit.properties @@ -0,0 +1,35 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +resolver=org.onap.ccsdk.sli.plugins.grtoolkit.resolver.SingleNodeHealthResolver +akka.conf.location=src/test/resources/akka.conf +adm.useSsl=false +adm.fqdn=localhost +adm.healthcheck=/adm/healthcheck +adm.port.http=9999 +adm.port.ssl=19999 +controller.credentials=admin:admin +controller.useSsl=false +controller.port.http=9999 +controller.port.ssl=19999 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +#site.identifier=TestODL diff --git a/plugins/grToolkit/provider/src/test/resources/single/akka.conf b/plugins/grToolkit/provider/src/test/resources/single/akka.conf new file mode 100644 index 000000000..ce46748ec --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/akka.conf @@ -0,0 +1,67 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.1.1:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/single/cluster.json b/plugins/grToolkit/provider/src/test/resources/single/cluster.json new file mode 100644 index 000000000..93360845f --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/cluster.json @@ -0,0 +1,17 @@ +{ + "request": { + "mbean": "akka:type=Cluster", + "type": "read" + }, + "value": { + "Leader": "akka.tcp://opendaylight-cluster-data@localhost:2550", + "Unreachable": "", + "Singleton": true, + "Available": true, + "MemberStatus": "Up", + "ClusterStatus": "{\n \"members\": [\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-1\"\n ],\n \"status\": \"Up\"\n }\n ],\n \"self-address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"unreachable\": []\n}\n", + "Members": "akka.tcp://opendaylight-cluster-data@localhost:2550" + }, + "timestamp": 1575393881, + "status": 200 +} diff --git a/plugins/grToolkit/provider/src/test/resources/single/default-config.json b/plugins/grToolkit/provider/src/test/resources/single/default-config.json new file mode 100644 index 000000000..d75998e5d --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/default-config.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-config", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-config", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-config", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/single/default-operational.json b/plugins/grToolkit/provider/src/test/resources/single/default-operational.json new file mode 100644 index 000000000..88a5d2c96 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/default-operational.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-operational", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-operational", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-operational", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/single/gr-toolkit.properties b/plugins/grToolkit/provider/src/test/resources/single/gr-toolkit.properties new file mode 100755 index 000000000..cc7820e90 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/gr-toolkit.properties @@ -0,0 +1,34 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +akka.conf.location=src/test/resources/single/akka.conf +adm.useSsl=false +adm.fqdn=localhost +adm.healthcheck=/adm/healthcheck +adm.port.http=9999 +adm.port.ssl=19999 +controller.credentials=admin:admin +controller.useSsl=false +controller.port.http=9999 +controller.port.ssl=19999 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +site.identifier=TestODL diff --git a/plugins/grToolkit/provider/src/test/resources/single/shard-manager.json b/plugins/grToolkit/provider/src/test/resources/single/shard-manager.json new file mode 100644 index 000000000..301184ef8 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/single/shard-manager.json @@ -0,0 +1,15 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "LocalShards": [ + "member-1-shard-default-config" + ], + "SyncStatus": true, + "MemberName": "member-1" + }, + "timestamp": 1575393918, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/six/akka.conf b/plugins/grToolkit/provider/src/test/resources/six/akka.conf new file mode 100644 index 000000000..9bda35d68 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/akka.conf @@ -0,0 +1,67 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.2:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.3:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.4:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.5:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.6:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/six/cluster.json b/plugins/grToolkit/provider/src/test/resources/six/cluster.json new file mode 100644 index 000000000..d6a54c8c8 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/cluster.json @@ -0,0 +1,17 @@ +{ + "request": { + "mbean": "akka:type=Cluster", + "type": "read" + }, + "value": { + "Leader": "akka.tcp://opendaylight-cluster-data@localhost:2550", + "Unreachable": "", + "Singleton": true, + "Available": true, + "MemberStatus": "Up", + "ClusterStatus": "{\n \"members\": [\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-1\"\n ],\n \"status\": \"Up\"\n },\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-2\"\n ],\n \"status\": \"Up\"\n },\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-3\"\n ],\n \"status\": \"Up\"\n }\n ],\n \"self-address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"unreachable\": []\n}\n", + "Members": "akka.tcp://opendaylight-cluster-data@localhost:2550" + }, + "timestamp": 1575393881, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/six/component-health.json b/plugins/grToolkit/provider/src/test/resources/six/component-health.json new file mode 100644 index 000000000..1ca01da42 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/component-health.json @@ -0,0 +1,7 @@ +{ + "output": { + "status": "200", + "served-by": "member-2", + "health": "HEALTHY" + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/six/default-config.json b/plugins/grToolkit/provider/src/test/resources/six/default-config.json new file mode 100644 index 000000000..d75998e5d --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/default-config.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-config", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-config", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-config", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/six/default-operational.json b/plugins/grToolkit/provider/src/test/resources/six/default-operational.json new file mode 100644 index 000000000..88a5d2c96 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/default-operational.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-operational", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-operational", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-operational", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/six/gr-toolkit.properties b/plugins/grToolkit/provider/src/test/resources/six/gr-toolkit.properties new file mode 100755 index 000000000..54c9af742 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/gr-toolkit.properties @@ -0,0 +1,34 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +akka.conf.location=src/test/resources/six/akka.conf +adm.useSsl=false +adm.fqdn=localhost +adm.healthcheck=/adm/healthcheck +adm.port.http=9999 +adm.port.ssl=19999 +controller.credentials=admin:admin +controller.useSsl=false +controller.port.http=9999 +controller.port.ssl=19999 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +site.identifier=TestODL diff --git a/plugins/grToolkit/provider/src/test/resources/six/shard-manager.json b/plugins/grToolkit/provider/src/test/resources/six/shard-manager.json new file mode 100644 index 000000000..301184ef8 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/shard-manager.json @@ -0,0 +1,15 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "LocalShards": [ + "member-1-shard-default-config" + ], + "SyncStatus": true, + "MemberName": "member-1" + }, + "timestamp": 1575393918, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/six/site-identifier.json b/plugins/grToolkit/provider/src/test/resources/six/site-identifier.json new file mode 100644 index 000000000..7599ceb15 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/six/site-identifier.json @@ -0,0 +1,7 @@ +{ + "output": { + "status": "200", + "id": "test-site", + "served-by": "member-1" + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/three/akka.conf b/plugins/grToolkit/provider/src/test/resources/three/akka.conf new file mode 100644 index 000000000..a6c151dd1 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/akka.conf @@ -0,0 +1,67 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +odl-cluster-data { + akka { + remote { + artery { + enabled = off + canonical.hostname = "127.0.0.1" + canonical.port = 2550 + } + netty.tcp { + hostname = "127.0.0.1" + port = 2550 + } + # when under load we might trip a false positive on the failure detector + # transport-failure-detector { + # heartbeat-interval = 4 s + # acceptable-heartbeat-pause = 16s + # } + } + + cluster { + # Remove ".tcp" when using artery. + seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.2:2550", "akka.tcp://opendaylight-cluster-data@127.0.0.3:2550"] + + roles = [ + "member-1" + ] + + } + + persistence { + # By default the snapshots/journal directories live in KARAF_HOME. You can choose to put it somewhere else by + # modifying the following two properties. The directory location specified may be a relative or absolute path. + # The relative path is always relative to KARAF_HOME. + + # snapshot-store.local.dir = "target/snapshots" + # journal.leveldb.dir = "target/journal" + + journal { + leveldb { + # Set native = off to use a Java-only implementation of leveldb. + # Note that the Java-only version is not currently considered by Akka to be production quality. + + # native = off + } + } + } + } +} diff --git a/plugins/grToolkit/provider/src/test/resources/three/cluster.json b/plugins/grToolkit/provider/src/test/resources/three/cluster.json new file mode 100644 index 000000000..d6a54c8c8 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/cluster.json @@ -0,0 +1,17 @@ +{ + "request": { + "mbean": "akka:type=Cluster", + "type": "read" + }, + "value": { + "Leader": "akka.tcp://opendaylight-cluster-data@localhost:2550", + "Unreachable": "", + "Singleton": true, + "Available": true, + "MemberStatus": "Up", + "ClusterStatus": "{\n \"members\": [\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-1\"\n ],\n \"status\": \"Up\"\n },\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-2\"\n ],\n \"status\": \"Up\"\n },\n {\n \"address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"roles\": [\n \"dc-default\",\n \"member-3\"\n ],\n \"status\": \"Up\"\n }\n ],\n \"self-address\": \"akka.tcp://opendaylight-cluster-data@localhost:2550\",\n \"unreachable\": []\n}\n", + "Members": "akka.tcp://opendaylight-cluster-data@localhost:2550" + }, + "timestamp": 1575393881, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/three/default-config.json b/plugins/grToolkit/provider/src/test/resources/three/default-config.json new file mode 100644 index 000000000..d75998e5d --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/default-config.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-config", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-config", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-config", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/three/default-operational.json b/plugins/grToolkit/provider/src/test/resources/three/default-operational.json new file mode 100644 index 000000000..88a5d2c96 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/default-operational.json @@ -0,0 +1,46 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=Shards,name=member-1-shard-default-operational,type=DistributedOperationalDatastore", + "type": "read" + }, + "value": { + "ReadWriteTransactionCount": 0, + "SnapshotIndex": 22, + "InMemoryJournalLogSize": 1, + "ReplicatedToAllIndex": -1, + "Leader": "member-1-shard-default-operational", + "LastIndex": 23, + "RaftState": "Leader", + "LastCommittedTransactionTime": "2019-12-03 16:36:39.413", + "LastApplied": 23, + "PeerAddresses": "", + "LastLogIndex": 23, + "LastLeadershipChangeTime": "2019-12-03 16:36:33.460", + "WriteOnlyTransactionCount": 0, + "FollowerInitialSyncStatus": false, + "FollowerInfo": [], + "FailedReadTransactionsCount": 0, + "Voting": true, + "StatRetrievalTime": "454.8 μs", + "CurrentTerm": 4, + "LastTerm": 4, + "FailedTransactionsCount": 0, + "PendingTxCommitQueueSize": 0, + "VotedFor": "member-1-shard-default-operational", + "SnapshotCaptureInitiated": false, + "CommittedTransactionsCount": 5, + "TxCohortCacheSize": 0, + "PeerVotingStates": "", + "LastLogTerm": 4, + "StatRetrievalError": null, + "CommitIndex": 23, + "SnapshotTerm": 4, + "AbortTransactionsCount": 0, + "ReadOnlyTransactionCount": 0, + "ShardName": "member-1-shard-default-operational", + "LeadershipChangeCount": 1, + "InMemoryJournalDataSize": 37 + }, + "timestamp": 1575393787, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/grToolkit/provider/src/test/resources/three/gr-toolkit.properties b/plugins/grToolkit/provider/src/test/resources/three/gr-toolkit.properties new file mode 100755 index 000000000..cc7820e90 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/gr-toolkit.properties @@ -0,0 +1,34 @@ +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2019 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= + +akka.conf.location=src/test/resources/single/akka.conf +adm.useSsl=false +adm.fqdn=localhost +adm.healthcheck=/adm/healthcheck +adm.port.http=9999 +adm.port.ssl=19999 +controller.credentials=admin:admin +controller.useSsl=false +controller.port.http=9999 +controller.port.ssl=19999 +controller.port.akka=2550 +mbean.cluster=/jolokia/read/akka:type=Cluster +mbean.shardManager=/jolokia/read/org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore +mbean.shard.config=/jolokia/read/org.opendaylight.controller:Category=Shards,name=%s,type=DistributedConfigDatastore +site.identifier=TestODL diff --git a/plugins/grToolkit/provider/src/test/resources/three/shard-manager.json b/plugins/grToolkit/provider/src/test/resources/three/shard-manager.json new file mode 100644 index 000000000..301184ef8 --- /dev/null +++ b/plugins/grToolkit/provider/src/test/resources/three/shard-manager.json @@ -0,0 +1,15 @@ +{ + "request": { + "mbean": "org.opendaylight.controller:Category=ShardManager,name=shard-manager-config,type=DistributedConfigDatastore", + "type": "read" + }, + "value": { + "LocalShards": [ + "member-1-shard-default-config" + ], + "SyncStatus": true, + "MemberName": "member-1" + }, + "timestamp": 1575393918, + "status": 200 +}
\ No newline at end of file diff --git a/plugins/properties-node/.gitignore b/plugins/properties-node/.gitignore new file mode 100755 index 000000000..b73caf31e --- /dev/null +++ b/plugins/properties-node/.gitignore @@ -0,0 +1,34 @@ +#####standard .git ignore entries##### + +## IDE Specific Files ## +org.eclipse.core.resources.prefs +.classpath +.project +.settings +.idea +.externalToolBuilders +maven-eclipse.xml +workspace + +## Compilation Files ## +*.class +**/target +target +target-ide +MANIFEST.MF + +## Misc Ignores (OS specific etc) ## +bin/ +dist +*~ +*.ipr +*.iml +*.iws +classes +out/ +.DS_STORE +.metadata + +## Folders which contain auto generated source code ## +yang-gen-config +yang-gen-sal diff --git a/plugins/properties-node/installer/pom.xml b/plugins/properties-node/installer/pom.xml new file mode 100755 index 000000000..4dc62faae --- /dev/null +++ b/plugins/properties-node/installer/pom.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: properties-node :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-properties-node</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/properties-node/installer/src/assembly/assemble_installer_zip.xml b/plugins/properties-node/installer/src/assembly/assemble_installer_zip.xml new file mode 100644 index 000000000..b6fd65582 --- /dev/null +++ b/plugins/properties-node/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,59 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/properties-node/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/properties-node/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..9cdc78879 --- /dev/null +++ b/plugins/properties-node/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,49 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/properties-node/installer/src/main/resources/scripts/install-feature.sh b/plugins/properties-node/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..0eab001d5 --- /dev/null +++ b/plugins/properties-node/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2017 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -d ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/properties-node/pom.xml b/plugins/properties-node/pom.xml new file mode 100755 index 000000000..71fffe4d3 --- /dev/null +++ b/plugins/properties-node/pom.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: properties-node</name> + <description>This is an implementation of DG Execute Node that reads property file and + puts the properties in the DG context</description> + + <modules> + <module>provider</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/properties-node/provider/pom.xml b/plugins/properties-node/provider/pom.xml new file mode 100755 index 000000000..fdfb4b3cb --- /dev/null +++ b/plugins/properties-node/provider/pom.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: properties-node :: ${project.artifactId}</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-core-artifacts</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.github.stefanbirkner</groupId> + <artifactId>system-rules</artifactId> + <version>1.19.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.jettison</groupId> + <artifactId>jettison</artifactId> + </dependency> + </dependencies> +</project> diff --git a/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/JsonParser.java b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/JsonParser.java new file mode 100644 index 000000000..89243a055 --- /dev/null +++ b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/JsonParser.java @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.prop; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class JsonParser { + + private static final Logger log = LoggerFactory.getLogger(JsonParser.class); + + private JsonParser() { + // Preventing instantiation of the same. + } + + @SuppressWarnings("unchecked") + public static Map<String, String> convertToProperties(String s) + throws SvcLogicException { + + checkNotNull(s, "Input should not be null."); + + try { + JSONObject json = new JSONObject(s); + Map<String, Object> wm = new HashMap<>(); + Iterator<String> ii = json.keys(); + while (ii.hasNext()) { + String key1 = ii.next(); + wm.put(key1, json.get(key1)); + } + + Map<String, String> mm = new HashMap<>(); + + while (!wm.isEmpty()) + for (String key : new ArrayList<>(wm.keySet())) { + Object o = wm.get(key); + wm.remove(key); + + if (o instanceof Boolean || o instanceof Number || o instanceof String) { + mm.put(key, o.toString()); + + log.info("Added property: {} : {}", key, o.toString()); + } else if (o instanceof JSONObject) { + JSONObject jo = (JSONObject) o; + Iterator<String> i = jo.keys(); + while (i.hasNext()) { + String key1 = i.next(); + wm.put(key + "." + key1, jo.get(key1)); + } + } else if (o instanceof JSONArray) { + JSONArray ja = (JSONArray) o; + mm.put(key + "_length", String.valueOf(ja.length())); + + log.info("Added property: {}_length: {}", key, String.valueOf(ja.length())); + + for (int i = 0; i < ja.length(); i++) + wm.put(key + '[' + i + ']', ja.get(i)); + } + } + return mm; + } catch (JSONException e) { + throw new SvcLogicException("Unable to convert JSON to properties " + e.getLocalizedMessage(), e); + } + } +} diff --git a/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/Parameters.java b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/Parameters.java new file mode 100644 index 000000000..99e4647d7 --- /dev/null +++ b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/Parameters.java @@ -0,0 +1,31 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.prop; + +import java.util.Set; + +public class Parameters { + public String fileName; + public String contextPrefix; + public Set<String> listNameList; + public boolean fileBasedParsing; +} diff --git a/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/PropertiesNode.java b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/PropertiesNode.java new file mode 100644 index 000000000..b4bc84747 --- /dev/null +++ b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/PropertiesNode.java @@ -0,0 +1,194 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.prop; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertiesNode implements SvcLogicJavaPlugin { + + private static final Logger log = LoggerFactory.getLogger(PropertiesNode.class); + + public void readProperties(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { + Parameters param = getParameters(paramMap); + Properties prop = new Properties(); + try { + File file = new File(param.fileName); + try (InputStream in = new FileInputStream(file)) { + Map<String, String> mm = null; + String pfx = param.contextPrefix != null ? param.contextPrefix + '.' : ""; + if (param.fileBasedParsing) { + byte[] data = new byte[(int) file.length()]; + if ("json".equalsIgnoreCase(getFileExtension(param.fileName))) { + in.read(data); + String str = new String(data, "UTF-8"); + mm = JsonParser.convertToProperties(str); + } else if ("xml".equalsIgnoreCase(getFileExtension(param.fileName))) { + in.read(data); + String str = new String(data, "UTF-8"); + mm = XmlParser.convertToProperties(str, param.listNameList); + } else { + prop.load(in); + for (Object key : prop.keySet()) { + String name = (String) key; + String value = prop.getProperty(name); + if (value != null && value.trim().length() > 0) { + ctx.setAttribute(pfx + name, getObfuscatedVal(value.trim())); + log.info("+++ " + pfx + name + ": [" + maskPassword(pfx + name, value) + "]"); + } + } + } + if (mm != null) { + for (Map.Entry<String, String> entry : mm.entrySet()) { + ctx.setAttribute(pfx + entry.getKey(), getObfuscatedVal(entry.getValue())); + log.info("+++ " + pfx + entry.getKey() + ": [" + + maskPassword(pfx + entry.getKey(), entry.getValue()) + "]"); + } + } + } else { + prop.load(in); + for (Object key : prop.keySet()) { + String name = (String) key; + String value = prop.getProperty(name); + if (value != null && value.trim().length() > 0) { + ctx.setAttribute(pfx + name, getObfuscatedVal(value.trim())); + log.info("+++ " + pfx + name + ": [" + maskPassword(pfx + name, value) + "]"); + } + } + } + } + } catch (IOException e) { + throw new SvcLogicException("Cannot read property file: " + param.fileName + ": " + e.getMessage(), e); + } + } + + /* Unobfuscate param value */ + private static String getObfuscatedVal(String paramValue) { + String resValue = paramValue; + if (paramValue != null && paramValue.startsWith("${") && paramValue.endsWith("}")) + { + String paramStr = paramValue.substring(2, paramValue.length()-1); + if (paramStr != null && paramStr.length() > 0) + { + String val = System.getenv(paramStr); + if (val != null && val.length() > 0) + { + resValue=val; + log.info("Obfuscated value RESET for param value:" + paramValue); + } + } + } + return resValue; + } + + /* + * Getting extension has to do the following "" --> "" "name" --> "" "name.txt" --> "txt" + * ".htpasswd" --> "" "name.with.many.dots.myext" --> "myext" + */ + private static String getFileExtension(String fileName) { + if (fileName.lastIndexOf(".") != -1 && fileName.lastIndexOf(".") != 0) { + return fileName.substring(fileName.lastIndexOf(".") + 1); + } else { + return ""; + } + } + + protected Parameters getParameters(Map<String, String> paramMap) throws SvcLogicException { + Parameters p = new Parameters(); + p.fileName = parseParam(paramMap, "fileName", true, null); + p.contextPrefix = parseParam(paramMap, "contextPrefix", false, null); + p.listNameList = getListNameList(paramMap); + String fileBasedParsingStr = paramMap.get("fileBasedParsing"); + p.fileBasedParsing = "true".equalsIgnoreCase(fileBasedParsingStr); + return p; + } + + protected Set<String> getListNameList(Map<String, String> paramMap) { + Set<String> ll = new HashSet<>(); + for (Map.Entry<String, String> entry : paramMap.entrySet()) { + if (entry.getKey().startsWith("listName")) { + ll.add(entry.getValue()); + } + } + return ll; + } + + private String parseParam(Map<String, String> paramMap, String name, boolean required, String def) + throws SvcLogicException { + String s = paramMap.get(name); + + if (s == null || s.trim().length() == 0) { + if (!required) { + return def; + } + throw new SvcLogicException("Parameter " + name + " is required in PropertiesNode"); + } + + s = s.trim(); + String value = ""; + int i = 0; + int i1 = s.indexOf('%'); + while (i1 >= 0) { + int i2 = s.indexOf('%', i1 + 1); + if (i2 < 0) { + throw new SvcLogicException("Cannot parse parameter " + name + ": " + s + ": no matching %"); + } + + String varName = s.substring(i1 + 1, i2); + String varValue = System.getenv(varName); + if (varValue == null) { + varValue = ""; + } + + value += s.substring(i, i1); + value += varValue; + + i = i2 + 1; + i1 = s.indexOf('%', i); + } + value += s.substring(i); + + log.info("Parameter " + name + ": " + maskPassword(name, value)); + return value; + } + + private static Object maskPassword(String name, Object value) { + String[] pwdNames = {"pwd", "passwd", "password", "Pwd", "Passwd", "Password"}; + for (String pwdName : pwdNames) { + if (name.contains(pwdName)) { + return "**********"; + } + } + return value; + } +} diff --git a/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/XmlParser.java b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/XmlParser.java new file mode 100644 index 000000000..f48a21e9d --- /dev/null +++ b/plugins/properties-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/prop/XmlParser.java @@ -0,0 +1,176 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.ccsdk.sli.plugins.prop; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.XMLConstants; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class XmlParser { + + private static final Logger log = LoggerFactory.getLogger(XmlParser.class); + + private XmlParser() { + // Preventing instantiation of the same. + } + + public static Map<String, String> convertToProperties(String s, Set<String> listNameList) + throws SvcLogicException { + + checkNotNull(s, "Input should not be null."); + + Handler handler = new Handler(listNameList); + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + SAXParser saxParser = factory.newSAXParser(); + InputStream in = new ByteArrayInputStream(s.getBytes()); + saxParser.parse(in, handler); + } catch (ParserConfigurationException | IOException | SAXException | NumberFormatException e) { + throw new SvcLogicException("Unable to convert XML to properties" + e.getLocalizedMessage(), e); + } + return handler.getProperties(); + } + + private static class Handler extends DefaultHandler { + + private Set<String> listNameList; + + private Map<String, String> properties = new HashMap<>(); + + StringBuilder currentName = new StringBuilder(); + StringBuilder currentValue = new StringBuilder(); + + public Handler(Set<String> listNameList) { + super(); + this.listNameList = listNameList; + if (this.listNameList == null) + this.listNameList = new HashSet<>(); + } + + public Map<String, String> getProperties() { + return properties; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + super.startElement(uri, localName, qName, attributes); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + if (currentName.length() > 0) + currentName.append(Character.toString('.')); + currentName.append(name); + + String listName = removeIndexes(currentName.toString()); + + if (listNameList.contains(listName)) { + String n = currentName.toString() + "_length"; + int len = getInt(properties, n); + properties.put(n, String.valueOf(len + 1)); + currentName.append("[").append(len).append("]"); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + String s = currentValue.toString().trim(); + if (s.length() > 0) { + properties.put(currentName.toString(), s); + + log.info("Added property: {} : {}", currentName, s); + currentValue = new StringBuilder(); + } + + int i1 = currentName.lastIndexOf("." + name); + if (i1 <= 0) + currentName = new StringBuilder(); + else + currentName = new StringBuilder(currentName.substring(0, i1)); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + + String value = new String(ch, start, length); + currentValue.append(value); + } + + private static int getInt(Map<String, String> mm, String name) { + String s = mm.get(name); + if (s == null) + return 0; + return Integer.parseInt(s); + } + + private String removeIndexes(String currentName) { + StringBuilder b = new StringBuilder(); + boolean add = true; + for (int i = 0; i < currentName.length(); i++) { + char c = currentName.charAt(i); + if (c == '[') + add = false; + else if (c == ']') + add = true; + else if (add) + b.append(Character.toString(c)); + } + return b.toString(); + } + } +} diff --git a/plugins/properties-node/provider/src/main/resources/OSGI-INF/blueprint/properties-node-blueprint.xml b/plugins/properties-node/provider/src/main/resources/OSGI-INF/blueprint/properties-node-blueprint.xml new file mode 100755 index 000000000..6e1d48efd --- /dev/null +++ b/plugins/properties-node/provider/src/main/resources/OSGI-INF/blueprint/properties-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="propNodeProvider" class="org.onap.ccsdk.sli.plugins.prop.PropertiesNode" /> + + + + <service ref="propNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.prop.PropertiesNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/properties-node/provider/src/main/resources/org/opendaylight/blueprint/properties-node-blueprint.xml b/plugins/properties-node/provider/src/main/resources/org/opendaylight/blueprint/properties-node-blueprint.xml new file mode 100755 index 000000000..6e1d48efd --- /dev/null +++ b/plugins/properties-node/provider/src/main/resources/org/opendaylight/blueprint/properties-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="propNodeProvider" class="org.onap.ccsdk.sli.plugins.prop.PropertiesNode" /> + + + + <service ref="propNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.prop.PropertiesNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestJsonParser.java b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestJsonParser.java new file mode 100644 index 000000000..0681124d9 --- /dev/null +++ b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestJsonParser.java @@ -0,0 +1,73 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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 jtest.org.onap.ccsdk.sli.plugins.prop; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.prop.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestJsonParser { + + private static final Logger log = LoggerFactory.getLogger(TestJsonParser.class); + + @Test + public void test() throws SvcLogicException, IOException { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.json")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Map<String, String> mm = JsonParser.convertToProperties(b.toString()); + + logProperties(mm); + + in.close(); + } + + @Test(expected = NullPointerException.class) + public void testNullString() throws SvcLogicException { + JsonParser.convertToProperties(null); + } + + private void logProperties(Map<String, String> mm) { + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) + ll.add((String) o); + Collections.sort(ll); + log.info("Properties:"); + for (String name : ll) + log.info("--- {}: {}", name, mm.get(name)); + } +} diff --git a/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestPropertiesNode.java b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestPropertiesNode.java new file mode 100644 index 000000000..a858c49b3 --- /dev/null +++ b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestPropertiesNode.java @@ -0,0 +1,608 @@ +package jtest.org.onap.ccsdk.sli.plugins.prop; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import static org.junit.Assert.assertEquals; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.prop.PropertiesNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestPropertiesNode { + + private static final Logger log = LoggerFactory.getLogger(TestPropertiesNode.class); + @Rule + public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + @Test + public void testJSONFileParsing() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("contextPrefix", "test-json"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-json.message"),"The provisioned access " + + "bandwidth is at or exceeds 50% of the total server capacity."); + } + + @Test + public void testJSONFileArrayParsing() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("contextPrefix", "test-json"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-json.equipment-data[0].max-server-speed"),"1600000"); + assertEquals(ctx.getAttribute("test-json.resource-state.used"),"1605000"); + assertEquals(ctx.getAttribute("test-json.resource-rule.service-model"),"DUMMY"); + assertEquals(ctx.getAttribute("test-json.resource-rule.endpoint-position"),"VCE-Cust"); + } + + @Test + public void testJSONFileParsingPrefixCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("contextPrefix", ""); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("equipment-data[0].max-server-speed"),"1600000"); + assertEquals(ctx.getAttribute("resource-state.used"),"1605000"); + assertEquals(ctx.getAttribute("resource-rule.service-model"),"DUMMY"); + assertEquals(ctx.getAttribute("resource-rule.endpoint-position"),"VCE-Cust"); + assertEquals(ctx.getAttribute("resource-rule.hard-limit-expression"),"max-server-" + + "speed * number-primary-servers"); + } + + @Test + public void testJSONFileParsingNoPrefix() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("equipment-data[0].max-server-speed"),"1600000"); + assertEquals(ctx.getAttribute("resource-state.used"),"1605000"); + assertEquals(ctx.getAttribute("resource-rule.service-model"),"DUMMY"); + assertEquals(ctx.getAttribute("resource-rule.endpoint-position"),"VCE-Cust"); + assertEquals(ctx.getAttribute("resource-rule.hard-limit-expression"),"max-server-" + + "speed * number-primary-servers"); + } + + @Test + public void testJSONFileParsingCtxCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("equipment-data[0].max-server-speed"),"1600000"); + assertEquals(ctx.getAttribute("resource-state.used"),"1605000"); + assertEquals(ctx.getAttribute("resource-rule.service-model"),"DUMMY"); + assertEquals(ctx.getAttribute("resource-rule.endpoint-position"),"VCE-Cust"); + assertEquals(ctx.getAttribute("resource-rule.hard-limit-expression"),"max-server-" + + "speed * number-primary-servers"); + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test(expected = SvcLogicException.class) + public void testToPropertiesInvalidJson() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test-invalid.json"); + p.put("contextPrefix", "invalid"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test + public void testTXTFileParsing() throws SvcLogicException { + + environmentVariables.set("deployer_pass", "sdncp-123"); + assertEquals("sdncp-123", System.getenv("deployer_pass")); + + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("contextPrefix", "test-txt"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-txt.service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("test-txt.service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("test-txt.operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("test-txt.service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("test-txt.service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + assertEquals(ctx.getAttribute("test-txt.obfuscated-var"), "sdncp-123"); + } + + @Test + public void testTXTFileParsingPrefixCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("contextPrefix", ""); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testTXTFileParsingNoPrefix() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testTXTFileParsingCtxCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test(expected = SvcLogicException.class) + public void testToPropertiesInvalidParam() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test(expected = SvcLogicException.class) + public void testToPropertiesNoParam() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test(expected = SvcLogicException.class) + public void testToPropertiesFilePathError() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/tests/resources/test.txt"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test + public void testXMLFileParsing() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", "test-xml"); + p.put("listName", "project.build"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-xml.project.modelVersion"),"4.0.0"); + } + + @Test + public void testXMLFileInnerParsing() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", "test-xml"); + p.put("listName", "project.modelVersion"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-xml.project.properties.project.build.sourceEncoding"),"UTF-8"); + assertEquals(ctx.getAttribute("test-xml.project.dependencies.dependency.scope"),"provided"); + assertEquals(ctx.getAttribute("test-xml.project.build.pluginManagement.plugins.plugin.configuration" + + ".lifecycleMappingMetadata.pluginExecutions.pluginExecution." + + "pluginExecutionFilter.versionRange"),"[1.2.0.100,)"); + assertEquals(ctx.getAttribute("test-xml.project.build.plugins.plugin.configuration." + + "instructions.Import-Package"),"*"); + } + + @Test + public void testXMLFileParsingPrefixCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", ""); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("project.properties.project.build.sourceEncoding"),"UTF-8"); + assertEquals(ctx.getAttribute("project.dependencies.dependency.scope"),"provided"); + assertEquals(ctx.getAttribute("project.build.pluginManagement.plugins.plugin.configuration" + + ".lifecycleMappingMetadata.pluginExecutions.pluginExecution." + + "pluginExecutionFilter.versionRange"),"[1.2.0.100,)"); + assertEquals(ctx.getAttribute("project.build.plugins.plugin.configuration." + + "instructions.Import-Package"),"*"); + } + + @Test + public void testXMLFileParsingNoPrefix() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("project.properties.project.build.sourceEncoding"),"UTF-8"); + assertEquals(ctx.getAttribute("project.dependencies.dependency.scope"),"provided"); + assertEquals(ctx.getAttribute("project.build.pluginManagement.plugins.plugin.configuration" + + ".lifecycleMappingMetadata.pluginExecutions.pluginExecution." + + "pluginExecutionFilter.versionRange"),"[1.2.0.100,)"); + assertEquals(ctx.getAttribute("project.build.plugins.plugin.configuration." + + "instructions.Import-Package"),"*"); + } + + @Test + public void testXMLFileParsingCtxCheck() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("project.properties.project.build.sourceEncoding"),"UTF-8"); + assertEquals(ctx.getAttribute("project.dependencies.dependency.scope"),"provided"); + assertEquals(ctx.getAttribute("project.build.pluginManagement.plugins.plugin.configuration" + + ".lifecycleMappingMetadata.pluginExecutions.pluginExecution." + + "pluginExecutionFilter.versionRange"),"[1.2.0.100,)"); + assertEquals(ctx.getAttribute("project.build.plugins.plugin.configuration." + + "instructions.Import-Package"),"*"); + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test(expected = SvcLogicException.class) + public void testToPropertiesInvalidXML() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test-invalid.xml"); + p.put("contextPrefix", "invalid"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test + public void testXMLFileParsingListName() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", "test-xml-listName"); + p.put("fileBasedParsing","true"); + p.put("listName", "project.build.pluginManagement"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-xml-listName.project.build." + + "pluginManagement.plugins.plugin.version"),null); + assertEquals(ctx.getAttribute("test-xml-listName.project.build." + + "plugins.plugin.groupId"),"org.apache.felix"); + } + + @Test + public void testXMLFileParsingListNameAnother() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", "test-xml-listName"); + p.put("fileBasedParsing","true"); + p.put("listName", "project.modelVersion"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-xml-listName.project.modelVersion"),null); + assertEquals(ctx.getAttribute("test-xml-listName.project.build." + + "plugins.plugin.groupId"),"org.apache.felix"); + } + + @Test + public void testTXTFileParsingNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("contextPrefix", "test-txt"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("test-txt.service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("test-txt.service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("test-txt.operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("test-txt.service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("test-txt.service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testTXTFileParsingPrefixCheckNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + p.put("contextPrefix", ""); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testTXTFileParsingNoPrefixNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testTXTFileParsingCtxCheckNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.txt"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + assertEquals(ctx.getAttribute("tmp.sdn-circuit-req-row_length"),"1"); + } + + @Test + public void testJSONFileArrayParsingNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.json"); + p.put("contextPrefix", "NotFileBased"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("NotFileBased.\"limit-value\""),"\"1920000\""); + assertEquals(ctx.getAttribute("NotFileBased.\"hard-limit-expression\""),"\"max-server-speed * number-primary-servers\","); + assertEquals(ctx.getAttribute("NotFileBased.\"test-inner-node\""),"\"Test-Value\""); + } + + @Test + public void testXMLFileInnerParsingNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test.xml"); + p.put("contextPrefix", "NotFileBased"); + p.put("listName", "project.modelVersion"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("NotFileBased.<name>RESTAPI"),"Call Node - Provider</name>"); + assertEquals(ctx.getAttribute("NotFileBased.<project"), + "xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + + " xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">"); + assertEquals(ctx.getAttribute("NotFileBased.openECOMP"),"SDN-C"); + assertEquals(ctx.getAttribute("NotFileBased.<ignore"),"/>"); + } + + @Test + public void testNoFileTypeNoPrefixNotFileBased() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test(expected = SvcLogicException.class) + public void testNoFileTypeParseReqError() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("file Name", "src/test/resources/test"); + p.put("fileBasedParsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } + + @Test + public void testNoFileTypeParseError() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<String, String>(); + p.put("fileName", "src/test/resources/test"); + p.put("file Based % Parsing","true"); + + PropertiesNode rcn = new PropertiesNode(); + rcn.readProperties(p, ctx); + + assertEquals(ctx.getAttribute("service-data.service-information.service-type"),"AVPN"); + assertEquals(ctx.getAttribute("service-configuration-notification-input.response-code"),"0"); + assertEquals(ctx.getAttribute("operational-data.avpn-ip-port-information.port-" + + "level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing"),"WRED"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.l1-customer-handoff"),"_1000BASELX"); + assertEquals(ctx.getAttribute("service-data.avpn-ip-port-information.avpn-" + + "access-information.vlan-tag-control"),"_1Q"); + } +} diff --git a/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestXmlParser.java b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestXmlParser.java new file mode 100644 index 000000000..7cd072ed8 --- /dev/null +++ b/plugins/properties-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/prop/TestXmlParser.java @@ -0,0 +1,121 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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 jtest.org.onap.ccsdk.sli.plugins.prop; + +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.prop.XmlParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class TestXmlParser { + + private static final Logger log = LoggerFactory.getLogger(TestXmlParser.class); + + @Test + public void test() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test3.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("project.dependencies.dependency"); + listNameList.add("project.build.plugins.plugin"); + listNameList.add("project.build.plugins.plugin.executions.execution"); + listNameList.add("project.build.pluginManagement.plugins.plugin"); + listNameList.add("project.build.pluginManagement." + + "plugins.plugin.configuration.lifecycleMappingMetadata.pluginExecutions.pluginExecution"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + logProperties(mm); + in.close(); + } + + @Test + public void testValidLength() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test3.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + + assertThat(mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[5]"), is("SET_RESET_LP")); + assertThat(mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport[0]"), is("SET_BVOIP_IN")); + + logProperties(mm); + in.close(); + } + + @Test(expected = SvcLogicException.class) + public void testInvalidLength() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("invalidlength.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + logProperties(mm); + in.close(); + } + + private void logProperties(Map<String, String> mm) { + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) + ll.add((String) o); + Collections.sort(ll); + + log.info("Properties:"); + for (String name : ll) + log.info("--- " + name + ": " + mm.get(name)); + } +} diff --git a/plugins/properties-node/provider/src/test/resources/invalidlength.xml b/plugins/properties-node/provider/src/test/resources/invalidlength.xml new file mode 100644 index 000000000..c086d564e --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/invalidlength.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport_length>a</VrfExport_length> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/properties-node/provider/src/test/resources/test b/plugins/properties-node/provider/src/test/resources/test new file mode 100644 index 000000000..79e8acff4 --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test @@ -0,0 +1,30 @@ +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-egress-class-queueing-policing-codes.cos2v-queueing-code = P +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-egress-class-queueing-policing-codes.cos3-queueing-code = W +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos2-queueing = WRED +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos2v-queueing = QueueLimit +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing = WRED +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos2-shaping = Disable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos2v-shaping = Enable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos3-shaping = Disable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-codes.cos2-shaping-code = W +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-codes.cos2v-shaping-code = P +service-configuration-notification-input.ack-final-indicator = Y +service-configuration-notification-input.response-code = 0 +service-configuration-notification-input.response-message = Plc Activation Failed: Device gblond2005me6 Sync-from Failed. Please check device IP address and NCS setup. +service-configuration-notification-input.service-information.service-instance-id = TEST7 +service-configuration-notification-input.service-information.service-type = AVPN +service-configuration-notification-input.svc-request-id = TEST7 +service-data.avpn-ip-port-information.avpn-access-information.access-circuit-id = DHEC.54831.170.ATI +service-data.avpn-ip-port-information.avpn-access-information.access-interface = _1G +service-data.avpn-ip-port-information.avpn-access-information.access-speed = 10000 +service-data.avpn-ip-port-information.avpn-access-information.access-speed-units = Kbps +service-data.avpn-ip-port-information.avpn-access-information.l1-customer-handoff = _1000BASELX +service-data.avpn-ip-port-information.avpn-access-information.managed-ce = N +service-data.avpn-ip-port-information.avpn-access-information.vlan-tag-control = _1Q +service-data.avpn-ip-port-information.clli = LONDENEH +service-data.avpn-ip-port-information.contracted-port-speed = 10000 +service-data.avpn-ip-port-information.contracted-port-speed-units = Kbps +service-data.avpn-ip-port-information.endpoint-information.bundle-id = 33 +service-data.avpn-ip-port-information.endpoint-information.interface-string = ae0 +service-data.service-information.service-instance-id = ICORESITE-2751508 +service-data.service-information.service-type = AVPN
\ No newline at end of file diff --git a/plugins/properties-node/provider/src/test/resources/test-invalid.json b/plugins/properties-node/provider/src/test/resources/test-invalid.json new file mode 100644 index 000000000..21af3ac1d --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test-invalid.json @@ -0,0 +1,29 @@ + "equipment-data": [ + { + "server-count": "4", + "max-server-speed": "1600000", + "number-primary-servers": "2", + "equipment-id": "Server1", + "server-model": "Unknown", + "server-id": "Server1", + "test-node" : { + "test-inner-node" : "Test-Value" + } + } + ], + "resource-state": { + "threshold-value": "1600000", + "last-added": "1605000", + "used": "1605000", + "limit-value": "1920000" + }, + "resource-rule": { + "endpoint-position": "VCE-Cust", + "soft-limit-expression": "0.6 * max-server-speed * number-primary-servers", + "resource-name": "Bandwidth", + "service-model": "DUMMY", + "hard-limit-expression": "max-server-speed * number-primary-servers", + "equipment-level": "Server" + }, + "message": "The provisioned access bandwidth is at or exceeds 50% of the total server capacity." +} diff --git a/plugins/properties-node/provider/src/test/resources/test-invalid.xml b/plugins/properties-node/provider/src/test/resources/test-invalid.xml new file mode 100644 index 000000000..50bd0fff2 --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test-invalid.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node</artifactId> + <version>6.0.0-SNAPSHOT</version> + </parent> + <artifactId>restapi-call-node-provider</artifactId> + <packaging>bundle</packaging> + <name>RESTAPI Call Node - Provider</name> + <url>http://maven.apache.org</url> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>3.1.4.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-common</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-provider</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-beans</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>xerces</groupId> + <artifactId>xerces</artifactId> + <version>2.4.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.s + + + <build> + <plugins> + <plugin> + <groupId>com.brocade.developer</groupId> + <artifactId>providermodule-plugin</artifactId> + <configuration> + <packageId>org.onap.ccsdk.sli.plugins</packageId> + <appName>restapi-call-node</appName> + </configuration> + <executions> + <execution> + <phase>process-sources</phase> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>org.onap.ccsdk.sli.plugins.restapicall</Bundle-SymbolicName> + <Export-Package>org.onap.ccsdk.sli.plugins.restapicall</Export-Package> + <Import-Package>*</Import-Package> + </instructions> + + <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation> + + </configuration> + + </plugin> + + + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + com.brocade.developer + </groupId> + <artifactId> + providermodule-plugin + </artifactId> + <versionRange> + [1.2.0.100-SNAPSHOT,) + </versionRange> + <goals> + <goal>process</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/plugins/properties-node/provider/src/test/resources/test.json b/plugins/properties-node/provider/src/test/resources/test.json new file mode 100644 index 000000000..75155393c --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test.json @@ -0,0 +1,30 @@ +{ + "equipment-data": [ + { + "server-count": "4", + "max-server-speed": "1600000", + "number-primary-servers": "2", + "equipment-id": "Server1", + "server-model": "Unknown", + "server-id": "Server1", + "test-node" : { + "test-inner-node" : "Test-Value" + } + } + ], + "resource-state": { + "threshold-value": "1600000", + "last-added": "1605000", + "used": "1605000", + "limit-value": "1920000" + }, + "resource-rule": { + "endpoint-position": "VCE-Cust", + "soft-limit-expression": "0.6 * max-server-speed * number-primary-servers", + "resource-name": "Bandwidth", + "service-model": "DUMMY", + "hard-limit-expression": "max-server-speed * number-primary-servers", + "equipment-level": "Server" + }, + "message": "The provisioned access bandwidth is at or exceeds 50% of the total server capacity." +} diff --git a/plugins/properties-node/provider/src/test/resources/test.txt b/plugins/properties-node/provider/src/test/resources/test.txt new file mode 100644 index 000000000..68b916cbb --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test.txt @@ -0,0 +1,31 @@ +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-egress-class-queueing-policing-codes.cos2v-queueing-code = P +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-egress-class-queueing-policing-codes.cos3-queueing-code = W +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos2-queueing = WRED +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos2v-queueing = QueueLimit +operational-data.avpn-ip-port-information.port-level-cos.queueing.pe-per-class-queueing-behaviors.cos3-queueing = WRED +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos2-shaping = Disable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos2v-shaping = Enable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-behaviors.cos3-shaping = Disable +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-codes.cos2-shaping-code = W +operational-data.avpn-ip-port-information.port-level-cos.shaping.pe-egress-per-class-shaping-codes.cos2v-shaping-code = P +service-configuration-notification-input.ack-final-indicator = Y +service-configuration-notification-input.response-code = 0 +service-configuration-notification-input.response-message = Plc Activation Failed: Device gblond2005me6 Sync-from Failed. Please check device IP address and NCS setup. +service-configuration-notification-input.service-information.service-instance-id = TEST7 +service-configuration-notification-input.service-information.service-type = AVPN +service-configuration-notification-input.svc-request-id = TEST7 +service-data.avpn-ip-port-information.avpn-access-information.access-circuit-id = DHEC.54831.170.ATI +service-data.avpn-ip-port-information.avpn-access-information.access-interface = _1G +service-data.avpn-ip-port-information.avpn-access-information.access-speed = 10000 +service-data.avpn-ip-port-information.avpn-access-information.access-speed-units = Kbps +service-data.avpn-ip-port-information.avpn-access-information.l1-customer-handoff = _1000BASELX +service-data.avpn-ip-port-information.avpn-access-information.managed-ce = N +service-data.avpn-ip-port-information.avpn-access-information.vlan-tag-control = _1Q +service-data.avpn-ip-port-information.clli = LONDENEH +service-data.avpn-ip-port-information.contracted-port-speed = 10000 +service-data.avpn-ip-port-information.contracted-port-speed-units = Kbps +service-data.avpn-ip-port-information.endpoint-information.bundle-id = 33 +service-data.avpn-ip-port-information.endpoint-information.interface-string = ae0 +service-data.service-information.service-instance-id = ICORESITE-2751508 +service-data.service-information.service-type = AVPN +obfuscated-var=${deployer_pass} diff --git a/plugins/properties-node/provider/src/test/resources/test.xml b/plugins/properties-node/provider/src/test/resources/test.xml new file mode 100644 index 000000000..19e3b7139 --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test.xml @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node</artifactId> + <version>6.0.0-SNAPSHOT</version> + </parent> + <artifactId>restapi-call-node-provider</artifactId> + <packaging>bundle</packaging> + <name>RESTAPI Call Node - Provider</name> + <url>http://maven.apache.org</url> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>3.1.4.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-common</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-provider</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-beans</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>xerces</groupId> + <artifactId>xerces</artifactId> + <version>2.4.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-signature</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.brocade.developer</groupId> + <artifactId>providermodule-plugin</artifactId> + <configuration> + <packageId>org.onap.ccsdk.sli.plugins</packageId> + <appName>restapi-call-node</appName> + </configuration> + <executions> + <execution> + <phase>process-sources</phase> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>org.onap.ccsdk.sli.plugins.restapicall</Bundle-SymbolicName> + <Export-Package>org.onap.ccsdk.sli.plugins.restapicall</Export-Package> + <Import-Package>*</Import-Package> + </instructions> + + <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation> + + </configuration> + + </plugin> + + + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + com.brocade.developer + </groupId> + <artifactId> + providermodule-plugin + </artifactId> + <versionRange> + [1.2.0.100,) + </versionRange> + <goals> + <goal>process</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/plugins/properties-node/provider/src/test/resources/test3.xml b/plugins/properties-node/provider/src/test/resources/test3.xml new file mode 100644 index 000000000..ade412681 --- /dev/null +++ b/plugins/properties-node/provider/src/test/resources/test3.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v4</Family> + <PeerGroupName>gp_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v6</Family> + <PeerGroupName>gp6_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/releases/0.5.0.yaml b/plugins/releases/0.5.0.yaml new file mode 100644 index 000000000..37ba918ab --- /dev/null +++ b/plugins/releases/0.5.0.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.5.0' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/160/' diff --git a/plugins/releases/0.5.1.yaml b/plugins/releases/0.5.1.yaml new file mode 100644 index 000000000..ca3ff0c0e --- /dev/null +++ b/plugins/releases/0.5.1.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.5.1' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/166/' diff --git a/plugins/releases/0.5.2.yaml b/plugins/releases/0.5.2.yaml new file mode 100644 index 000000000..4c7946b28 --- /dev/null +++ b/plugins/releases/0.5.2.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.5.2' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/173/' diff --git a/plugins/releases/0.6.0.yaml b/plugins/releases/0.6.0.yaml new file mode 100644 index 000000000..8f32b423b --- /dev/null +++ b/plugins/releases/0.6.0.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.6.0' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/187/' diff --git a/plugins/releases/0.6.1.yaml b/plugins/releases/0.6.1.yaml new file mode 100644 index 000000000..7739acdeb --- /dev/null +++ b/plugins/releases/0.6.1.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.6.1' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/204/' diff --git a/plugins/releases/0.7.0.yaml b/plugins/releases/0.7.0.yaml new file mode 100644 index 000000000..e86406631 --- /dev/null +++ b/plugins/releases/0.7.0.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.7.0' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/300/' diff --git a/plugins/releases/0.7.1.yaml b/plugins/releases/0.7.1.yaml new file mode 100644 index 000000000..990922216 --- /dev/null +++ b/plugins/releases/0.7.1.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '0.7.1' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/301/' diff --git a/plugins/releases/1.0.0.yaml b/plugins/releases/1.0.0.yaml new file mode 100644 index 000000000..ca1f69cf8 --- /dev/null +++ b/plugins/releases/1.0.0.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '1.0.0' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/302/' diff --git a/plugins/releases/1.0.1.yaml b/plugins/releases/1.0.1.yaml new file mode 100644 index 000000000..bb233a9a5 --- /dev/null +++ b/plugins/releases/1.0.1.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '1.0.1' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/303/' diff --git a/plugins/releases/1.1.0.yaml b/plugins/releases/1.1.0.yaml new file mode 100644 index 000000000..75773b2c3 --- /dev/null +++ b/plugins/releases/1.1.0.yaml @@ -0,0 +1,5 @@ +--- +distribution_type: 'maven' +version: '1.1.0' +project: 'ccsdk-sli-plugins' +log_dir: 'ccsdk-sli-plugins-maven-stage-master/304/' diff --git a/plugins/restapi-call-node/.gitignore b/plugins/restapi-call-node/.gitignore new file mode 100755 index 000000000..b73caf31e --- /dev/null +++ b/plugins/restapi-call-node/.gitignore @@ -0,0 +1,34 @@ +#####standard .git ignore entries##### + +## IDE Specific Files ## +org.eclipse.core.resources.prefs +.classpath +.project +.settings +.idea +.externalToolBuilders +maven-eclipse.xml +workspace + +## Compilation Files ## +*.class +**/target +target +target-ide +MANIFEST.MF + +## Misc Ignores (OS specific etc) ## +bin/ +dist +*~ +*.ipr +*.iml +*.iws +classes +out/ +.DS_STORE +.metadata + +## Folders which contain auto generated source code ## +yang-gen-config +yang-gen-sal diff --git a/plugins/restapi-call-node/installer/pom.xml b/plugins/restapi-call-node/installer/pom.xml new file mode 100755 index 000000000..fbf2cff60 --- /dev/null +++ b/plugins/restapi-call-node/installer/pom.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: restapi-call-node :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-restapi-call-node</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/restapi-call-node/installer/src/assembly/assemble_installer_zip.xml b/plugins/restapi-call-node/installer/src/assembly/assemble_installer_zip.xml new file mode 100644 index 000000000..3bed4b5ef --- /dev/null +++ b/plugins/restapi-call-node/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,59 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/restapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/restapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..35e79db18 --- /dev/null +++ b/plugins/restapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,49 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/restapi-call-node/installer/src/main/resources/scripts/install-feature.sh b/plugins/restapi-call-node/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..6e3d4169d --- /dev/null +++ b/plugins/restapi-call-node/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2017 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -nd ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/restapi-call-node/pom.xml b/plugins/restapi-call-node/pom.xml new file mode 100755 index 000000000..c5b753305 --- /dev/null +++ b/plugins/restapi-call-node/pom.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: restapi-call-node</name> + <description>This is an implementation of DG Execute Node that makes a call to an external REST API</description> + + <modules> + <module>provider</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/restapi-call-node/provider/pom.xml b/plugins/restapi-call-node/provider/pom.xml new file mode 100755 index 000000000..89711519a --- /dev/null +++ b/plugins/restapi-call-node/provider/pom.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: restapi-call-node :: ${project.artifactId}</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-core-artifacts</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.bundles.repackaged</groupId> + <artifactId>jersey-guava</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.security</groupId> + <artifactId>oauth1-signature</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.security</groupId> + <artifactId>oauth1-client</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.jettison</groupId> + <artifactId>jettison</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-multipart</artifactId> + </dependency> + <dependency> + <groupId>org.onap.logging-analytics</groupId> + <artifactId>logging-filter-base</artifactId> + </dependency> + <!-- For test --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.github.stefanbirkner</groupId> + <artifactId>system-rules</artifactId> + <version>1.19.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-servlet</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-grizzly2-http</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/AuthType.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/AuthType.java new file mode 100644 index 000000000..851dc9cc8 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/AuthType.java @@ -0,0 +1,19 @@ +package org.onap.ccsdk.sli.plugins.restapicall; + +public enum AuthType { + NONE, BASIC, DIGEST, OAUTH, Unspecified; + + public static AuthType fromString(String s) { + if ("basic".equalsIgnoreCase(s)) + return BASIC; + if ("digest".equalsIgnoreCase(s)) + return DIGEST; + if ("oauth".equalsIgnoreCase(s)) + return OAUTH; + if ("none".equalsIgnoreCase(s)) + return NONE; + if ("unspecified".equalsIgnoreCase(s)) + return Unspecified; + throw new IllegalArgumentException("Invalid value for format: " + s); + } +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Format.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Format.java new file mode 100644 index 000000000..1578ee320 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Format.java @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +public enum Format { + JSON, XML, NONE; + + public static Format fromString(String s) { + if ("json".equalsIgnoreCase(s)) + return JSON; + if ("xml".equalsIgnoreCase(s)) + return XML; + if ("none".equalsIgnoreCase(s)) + return NONE; + throw new IllegalArgumentException("Invalid value for format: " + s); + } +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpMethod.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpMethod.java new file mode 100644 index 000000000..b2f618a1c --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpMethod.java @@ -0,0 +1,43 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +public enum HttpMethod { + GET, POST, PUT, DELETE, PATCH; + + public static HttpMethod fromString(String s) { + if (s == null) + return null; + if (("get").equalsIgnoreCase(s)) + return GET; + if (("post").equalsIgnoreCase(s)) + return POST; + if (("put").equalsIgnoreCase(s)) + return PUT; + if (("delete").equalsIgnoreCase(s)) + return DELETE; + if (("patch").equalsIgnoreCase(s)) + return PATCH; + throw new IllegalArgumentException("Invalid value for HTTP Method: " + s); + } +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpResponse.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpResponse.java new file mode 100644 index 000000000..574089528 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/HttpResponse.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.MultivaluedMap; + +public class HttpResponse { + public int code; + public String message; + public String body; + public MultivaluedMap<String, String> headers; + public Map<String, List<String>> headers2; +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/JsonParser.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/JsonParser.java new file mode 100644 index 000000000..60d43df7a --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/JsonParser.java @@ -0,0 +1,153 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class JsonParser { + + private static final Logger log = LoggerFactory.getLogger(JsonParser.class); + + private JsonParser() { + // Preventing instantiation of the same. + } + + + private static void handleJsonArray(String key, Map<String, Object> jArrayMap, JSONArray jsonArr) throws JSONException { + JSONObject jsonObj; + JSONArray subJsonArr; + boolean stripKey = false; + + for (int i = 0, length = jsonArr.length(); i < length; i++) { + if (stripKey) + key = key.substring(0, key.length()-3); + + subJsonArr = jsonArr.optJSONArray(i); + if (subJsonArr != null) { + key = StringUtils.trimToEmpty(key) + "[" + i + "]"; + jArrayMap.putIfAbsent(key + "_length", String.valueOf(subJsonArr.length())); + handleJsonArray(key, jArrayMap, subJsonArr); + stripKey = true; + continue; + } + + jsonObj = jsonArr.optJSONObject(i); + if (jsonObj != null) { + Iterator<String> ii = jsonObj.keys(); + while (ii.hasNext()) { + String nodeKey = ii.next(); + String key1 = "[" + i + "]." + nodeKey; + String[] subKey = key1.split(":"); + if (subKey.length == 2) { + jArrayMap.putIfAbsent(subKey[1], jsonObj.get(nodeKey)); + } else { + jArrayMap.putIfAbsent(key1, jsonObj.get(nodeKey)); + } + } + } + else { + jArrayMap.putIfAbsent(StringUtils.trimToEmpty(key), jsonArr); + break; + } + } + } + + @SuppressWarnings("unchecked") + public static Map<String, String> convertToProperties(String s) + throws SvcLogicException { + + checkNotNull(s, "Input should not be null."); + + try { + Map<String, Object> wm = new HashMap<>(); + JSONObject json; + JSONArray jsonArr; + //support top level list in json response + if (s.startsWith("[")) { + jsonArr = new JSONArray(s); + wm.put("_length", String.valueOf(jsonArr.length())); + handleJsonArray(null, wm, jsonArr); + } else { + json = new JSONObject(s); + Iterator<String> ii = json.keys(); + while (ii.hasNext()) { + String key1 = ii.next(); + String[] subKey = key1.split(":"); + if (subKey.length == 2) { + wm.put(subKey[1], json.get(key1)); + } else { + wm.put(key1, json.get(key1)); + } + } + } + + Map<String, String> mm = new HashMap<>(); + while (!wm.isEmpty()) { + for (String key : new ArrayList<>(wm.keySet())) { + Object o = wm.get(key); + wm.remove(key); + + if (o instanceof Boolean || o instanceof Number || o instanceof String) { + mm.put(key, o.toString()); + log.info("Added property: {} : {}", key, o.toString()); + } else if (o instanceof JSONObject) { + JSONObject jo = (JSONObject) o; + Iterator<String> i = jo.keys(); + while (i.hasNext()) { + String key1 = i.next(); + String[] subKey = key1.split(":"); + if (subKey.length == 2) { + wm.put(key + "." + subKey[1], jo.get(key1)); + } else { + wm.put(key + "." + key1, jo.get(key1)); + } + } + } else if (o instanceof JSONArray) { + JSONArray ja = (JSONArray) o; + mm.put(key + "_length", String.valueOf(ja.length())); + log.info("Added property: {}_length: {}", key, ja.length()); + + for (int i = 0; i < ja.length(); i++) { + wm.put(key + '[' + i + ']', ja.get(i)); + } + } + } + } + return mm; + } catch (JSONException e) { + throw new SvcLogicException("Unable to convert JSON to properties" + e.getLocalizedMessage(), e); + } + } + +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Parameters.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Parameters.java new file mode 100755 index 000000000..9b542af91 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/Parameters.java @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import java.util.Set; + +public class Parameters { + public String templateFileName; + public String restapiUrl; + public String restapiUrlSuffix; + public String restapiUser; + public String restapiPassword; + public Format format; + public String contentType; + public HttpMethod httpMethod; + public String responsePrefix; + public Set<String> listNameList; + public boolean skipSending; + public boolean convertResponse; + public String keyStoreFileName; + public String keyStorePassword; + public boolean ssl; + public String customHttpHeaders; + public String partner; + public Boolean dumpHeaders; + public String requestBody; + public String oAuthConsumerKey; + public String oAuthConsumerSecret; + public String oAuthSignatureMethod; + public String oAuthVersion; + public AuthType authtype; + public Boolean returnRequestPayload; + public String accept; + public boolean multipartFormData; + public String multipartFile; + public String targetEntity; +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/PartnerDetails.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/PartnerDetails.java new file mode 100755 index 000000000..1cd4b99bb --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/PartnerDetails.java @@ -0,0 +1,14 @@ +package org.onap.ccsdk.sli.plugins.restapicall;
+
+public class PartnerDetails {
+ protected String username;
+ protected String password;
+ protected String url;
+
+ public PartnerDetails(String username, String password, String url) {
+ this.username = username;
+ this.password = password;
+ this.url = url;
+ }
+
+}
diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java new file mode 100755 index 000000000..3d7042493 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RestapiCallNode.java @@ -0,0 +1,1306 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static java.lang.Boolean.valueOf; +import static javax.ws.rs.client.Entity.entity; +import static org.onap.ccsdk.sli.plugins.restapicall.AuthType.fromString; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.net.SocketException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.EntityTag; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import org.apache.commons.lang3.StringUtils; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.HttpUrlConnectorProvider; +import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; +import org.glassfish.jersey.client.oauth1.ConsumerCredentials; +import org.glassfish.jersey.client.oauth1.OAuth1ClientSupport; +import org.glassfish.jersey.media.multipart.MultiPart; +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.logging.filter.base.HttpURLConnectionMetricUtil; +import org.onap.logging.filter.base.MetricLogClientFilter; +import org.onap.logging.filter.base.ONAPComponents; +import org.onap.logging.ref.slf4j.ONAPLogConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +public class RestapiCallNode implements SvcLogicJavaPlugin { + + protected static final String PARTNERS_FILE_NAME = "partners.json"; + protected static final String UEB_PROPERTIES_FILE_NAME = "ueb.properties"; + protected static final String DEFAULT_PROPERTIES_DIR = "/opt/onap/ccsdk/data/properties"; + protected static final String PROPERTIES_DIR_KEY = "SDNC_CONFIG_DIR"; + protected static final int DEFAULT_HTTP_CONNECT_TIMEOUT_MS = 30000; // 30 seconds + protected static final int DEFAULT_HTTP_READ_TIMEOUT_MS = 600000; // 10 minutes + + private static final Logger log = LoggerFactory.getLogger(RestapiCallNode.class); + private String uebServers; + private String defaultUebTemplateFileName = "/opt/bvc/restapi/templates/default-ueb-message.json"; + + private String responseReceivedMessage = "Response received. Time: {}"; + private String responseHttpCodeMessage = "HTTP response code: {}"; + private String requestPostingException = "Exception while posting http request to client "; + protected static final String skipSendingMessage = "skipSending"; + protected static final String responsePrefix = "responsePrefix"; + protected static final String restapiUrlString = "restapiUrl"; + protected static final String restapiUserKey = "restapiUser"; + protected static final String restapiPasswordKey = "restapiPassword"; + protected Integer httpConnectTimeout; + protected Integer httpReadTimeout; + + protected HashMap<String, PartnerDetails> partnerStore; + private static final Pattern retryPattern = Pattern.compile(".*,(http|https):.*"); + + public RestapiCallNode() { + String configDir = System.getProperty(PROPERTIES_DIR_KEY, DEFAULT_PROPERTIES_DIR); + try { + String jsonString = readFile(configDir + "/" + PARTNERS_FILE_NAME); + JSONObject partners = new JSONObject(jsonString); + partnerStore = new HashMap<>(); + loadPartners(partners); + log.info("Partners support enabled"); + } catch (Exception e) { + log.warn("Partners file could not be read, Partner support will not be enabled. " + e.getMessage()); + } + + try (FileInputStream in = new FileInputStream(configDir + "/" + UEB_PROPERTIES_FILE_NAME)) { + Properties props = new Properties(); + props.load(in); + uebServers = props.getProperty("servers"); + log.info("UEB support enabled"); + } catch (Exception e) { + log.warn("UEB properties could not be read, UEB support will not be enabled. " + e.getMessage()); + } + httpConnectTimeout = readOptionalInteger("HTTP_CONNECT_TIMEOUT_MS",DEFAULT_HTTP_CONNECT_TIMEOUT_MS); + httpReadTimeout = readOptionalInteger("HTTP_READ_TIMEOUT_MS",DEFAULT_HTTP_READ_TIMEOUT_MS); + } + + @SuppressWarnings("unchecked") + protected void loadPartners(JSONObject partners) { + Iterator<String> keys = partners.keys(); + String partnerUserKey = "user"; + String partnerPasswordKey = "password"; + String partnerUrlKey = "url"; + + while (keys.hasNext()) { + String partnerKey = keys.next(); + try { + JSONObject partnerObject = (JSONObject) partners.get(partnerKey); + if (partnerObject.has(partnerUserKey) && partnerObject.has(partnerPasswordKey)) { + String url = null; + if (partnerObject.has(partnerUrlKey)) { + url = partnerObject.getString(partnerUrlKey); + } + String userName = partnerObject.getString(partnerUserKey); + String password = partnerObject.getString(partnerPasswordKey); + PartnerDetails details = new PartnerDetails(userName, getObfuscatedVal(password), url); + partnerStore.put(partnerKey, details); + log.info("mapped partner using partner key " + partnerKey); + } else { + log.info("Partner " + partnerKey + " is missing required keys, it won't be mapped"); + } + } catch (JSONException e) { + log.info("Couldn't map the partner using partner key " + partnerKey, e); + } + } + } + + /* Unobfuscate param value */ + private static String getObfuscatedVal(String paramValue) { + String resValue = paramValue; + if (paramValue != null && paramValue.startsWith("${") && paramValue.endsWith("}")) + { + String paramStr = paramValue.substring(2, paramValue.length()-1); + if (paramStr != null && paramStr.length() > 0) + { + String val = System.getenv(paramStr); + if (val != null && val.length() > 0) + { + resValue=val; + log.info("Obfuscated value RESET for param value:" + paramValue); + } + } + } + return resValue; + } + + /** + * Returns parameters from the parameter map. + * + * @param paramMap parameter map + * @param p parameters instance + * @return parameters filed instance + * @throws SvcLogicException when svc logic exception occurs + */ + public static Parameters getParameters(Map<String, String> paramMap, Parameters p) throws SvcLogicException { + + p.templateFileName = parseParam(paramMap, "templateFileName", false, null); + p.requestBody = parseParam(paramMap, "requestBody", false, null); + p.restapiUrl = parseParam(paramMap, restapiUrlString, true, null); + p.restapiUrlSuffix = parseParam(paramMap, "restapiUrlSuffix", false, null); + if (p.restapiUrlSuffix != null) { + p.restapiUrl = p.restapiUrl + p.restapiUrlSuffix; + } + + p.restapiUrl = UriBuilder.fromUri(p.restapiUrl).toTemplate(); + validateUrl(p.restapiUrl); + + p.restapiUser = parseParam(paramMap, restapiUserKey, false, null); + p.restapiPassword = parseParam(paramMap, restapiPasswordKey, false, null); + p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null); + p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null); + p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null); + p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null); + p.contentType = parseParam(paramMap, "contentType", false, null); + p.format = Format.fromString(parseParam(paramMap, "format", false, "json")); + p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified")); + p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post")); + p.responsePrefix = parseParam(paramMap, responsePrefix, false, null); + p.listNameList = getListNameList(paramMap); + String skipSendingStr = paramMap.get(skipSendingMessage); + p.skipSending = "true".equalsIgnoreCase(skipSendingStr); + p.convertResponse = valueOf(parseParam(paramMap, "convertResponse", false, "true")); + p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName", false, null); + p.keyStorePassword = parseParam(paramMap, "keyStorePassword", false, null); + p.ssl = p.keyStoreFileName != null && p.keyStorePassword != null; + p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", false, null); + p.partner = parseParam(paramMap, "partner", false, null); + p.dumpHeaders = valueOf(parseParam(paramMap, "dumpHeaders", false, null)); + p.returnRequestPayload = valueOf(parseParam(paramMap, "returnRequestPayload", false, null)); + p.accept = parseParam(paramMap, "accept", false, null); + p.multipartFormData = valueOf(parseParam(paramMap, "multipartFormData", false, "false")); + p.multipartFile = parseParam(paramMap, "multipartFile", false, null); + p.targetEntity = parseParam(paramMap, "targetEntity", false, null); + return p; + } + + /** + * Validates the given URL in the parameters. + * + * @param restapiUrl rest api URL + * @throws SvcLogicException when URL validation fails + */ + private static void validateUrl(String restapiUrl) throws SvcLogicException { + if (containsMultipleUrls(restapiUrl)) { + String[] urls = getMultipleUrls(restapiUrl); + for (String url : urls) { + validateUrl(url); + } + } else { + try { + URI.create(restapiUrl); + } catch (IllegalArgumentException e) { + throw new SvcLogicException("Invalid input of url " + e.getLocalizedMessage(), e); + } + } + } + + /** + * Returns the list of list name. + * + * @param paramMap parameters map + * @return list of list name + */ + private static Set<String> getListNameList(Map<String, String> paramMap) { + Set<String> ll = new HashSet<>(); + for (Map.Entry<String, String> entry : paramMap.entrySet()) { + if (entry.getKey().startsWith("listName")) { + ll.add(entry.getValue()); + } + } + return ll; + } + + /** + * Parses the parameter string map of property, validates if required, assigns default value if + * present and returns the value. + * + * @param paramMap string param map + * @param name name of the property + * @param required if value required + * @param def default value + * @return value of the property + * @throws SvcLogicException if required parameter value is empty + */ + public static String parseParam(Map<String, String> paramMap, String name, boolean required, String def) + throws SvcLogicException { + String s = paramMap.get(name); + + if (s == null || s.trim().length() == 0) { + if (!required) { + return def; + } + throw new SvcLogicException("Parameter " + name + " is required in RestapiCallNode"); + } + + s = s.trim(); + StringBuilder value = new StringBuilder(); + int i = 0; + int i1 = s.indexOf('%'); + while (i1 >= 0) { + int i2 = s.indexOf('%', i1 + 1); + if (i2 < 0) { + break; + } + + String varName = s.substring(i1 + 1, i2); + String varValue = System.getenv(varName); + if (varValue == null) { + varValue = "%" + varName + "%"; + } + + value.append(s.substring(i, i1)); + value.append(varValue); + + i = i2 + 1; + i1 = s.indexOf('%', i); + } + value.append(s.substring(i)); + + log.info("Parameter {}: [{}]", name, maskPassword(name, value)); + + return value.toString(); + } + + private static Object maskPassword(String name, Object value) { + String[] pwdNames = {"pwd", "passwd", "password", "Pwd", "Passwd", "Password"}; + for (String pwdName : pwdNames) { + if (name.contains(pwdName)) { + return "**********"; + } + } + return value; + } + + /** + * Allows Directed Graphs the ability to interact with REST APIs. + * + * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead> + * <th>parameter</th> + * <th>Mandatory/Optional</th> + * <th>description</th> + * <th>example values</th></thead> <tbody> + * <tr> + * <td>templateFileName</td> + * <td>Optional</td> + * <td>full path to template file that can be used to build a request</td> + * <td>/sdncopt/bvc/restapi/templates/vnf_service-configuration-operation_minimal.json</td> + * </tr> + * <tr> + * <td>restapiUrl</td> + * <td>Mandatory</td> + * <td>url to send the request to</td> + * <td>https://sdncodl:8543/restconf/operations/L3VNF-API:create-update-vnf-request</td> + * </tr> + * <tr> + * <td>restapiUser</td> + * <td>Optional</td> + * <td>user name to use for http basic authentication</td> + * <td>sdnc_ws</td> + * </tr> + * <tr> + * <td>restapiPassword</td> + * <td>Optional</td> + * <td>unencrypted password to use for http basic authentication</td> + * <td>plain_password</td> + * </tr> + * <tr> + * <td>oAuthConsumerKey</td> + * <td>Optional</td> + * <td>Consumer key to use for http oAuth authentication</td> + * <td>plain_key</td> + * </tr> + * <tr> + * <td>oAuthConsumerSecret</td> + * <td>Optional</td> + * <td>Consumer secret to use for http oAuth authentication</td> + * <td>plain_secret</td> + * </tr> + * <tr> + * <td>oAuthSignatureMethod</td> + * <td>Optional</td> + * <td>Consumer method to use for http oAuth authentication</td> + * <td>method</td> + * </tr> + * <tr> + * <td>oAuthVersion</td> + * <td>Optional</td> + * <td>Version http oAuth authentication</td> + * <td>version</td> + * </tr> + * <tr> + * <td>contentType</td> + * <td>Optional</td> + * <td>http content type to set in the http header</td> + * <td>usually application/json or application/xml</td> + * </tr> + * <tr> + * <td>format</td> + * <td>Optional</td> + * <td>should match request body format</td> + * <td>json or xml</td> + * </tr> + * <tr> + * <td>httpMethod</td> + * <td>Optional</td> + * <td>http method to use when sending the request</td> + * <td>get post put delete patch</td> + * </tr> + * <tr> + * <td>responsePrefix</td> + * <td>Optional</td> + * <td>location the response will be written to in context memory</td> + * <td>tmp.restapi.result</td> + * </tr> + * <tr> + * <td>listName[i]</td> + * <td>Optional</td> + * <td>Used for processing XML responses with repeating + * elements.</td>vpn-information.vrf-details + * <td></td> + * </tr> + * <tr> + * <td>skipSending</td> + * <td>Optional</td> + * <td></td> + * <td>true or false</td> + * </tr> + * <tr> + * <td>convertResponse</td> + * <td>Optional</td> + * <td>whether the response should be converted</td> + * <td>true or false</td> + * </tr> + * <tr> + * <td>customHttpHeaders</td> + * <td>Optional</td> + * <td>a list additional http headers to be passed in, follow the format in the example</td> + * <td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td> + * </tr> + * <tr> + * <td>dumpHeaders</td> + * <td>Optional</td> + * <td>when true writes http header content to context memory</td> + * <td>true or false</td> + * </tr> + * <tr> + * <td>partner</td> + * <td>Optional</td> + * <td>used to retrieve username, password and url if partner store exists</td> + * <td>aaf</td> + * </tr> + * <tr> + * <td>returnRequestPayload</td> + * <td>Optional</td> + * <td>used to return payload built in the request</td> + * <td>true or false</td> + * </tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + * @see String#split(String, int) + */ + public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { + sendRequest(paramMap, ctx, null); + } + + protected void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, RetryPolicy retryPolicy) + throws SvcLogicException { + + HttpResponse r = new HttpResponse(); + try { + handlePartner(paramMap); + Parameters p = getParameters(paramMap, new Parameters()); + if(p.targetEntity != null && !p.targetEntity.isEmpty()) { + MDC.put(ONAPLogConstants.MDCs.TARGET_ENTITY, p.targetEntity); + } + if (containsMultipleUrls(p.restapiUrl) && retryPolicy == null) { + String[] urls = getMultipleUrls(p.restapiUrl); + retryPolicy = new RetryPolicy(urls, urls.length * 2); + p.restapiUrl = urls[0]; + } + String pp = p.responsePrefix != null ? p.responsePrefix + '.' : ""; + + String req = null; + if (p.templateFileName != null) { + String reqTemplate = readFile(p.templateFileName); + req = buildXmlJsonRequest(ctx, reqTemplate, p.format); + } else if (p.requestBody != null) { + req = p.requestBody; + } + r = sendHttpRequest(req, p); + setResponseStatus(ctx, p.responsePrefix, r); + + if (p.dumpHeaders && r.headers != null) { + for (Entry<String, List<String>> a : r.headers.entrySet()) { + ctx.setAttribute(pp + "header." + a.getKey(), StringUtils.join(a.getValue(), ",")); + } + } + + if (p.returnRequestPayload && req != null) { + ctx.setAttribute(pp + "httpRequest", req); + } + + if (r.body != null && r.body.trim().length() > 0) { + ctx.setAttribute(pp + "httpResponse", r.body); + + if (p.convertResponse) { + Map<String, String> mm = null; + if (p.format == Format.XML) { + mm = XmlParser.convertToProperties(r.body, p.listNameList); + } else if (p.format == Format.JSON) { + mm = JsonParser.convertToProperties(r.body); + } + + if (mm != null) { + for (Map.Entry<String, String> entry : mm.entrySet()) { + ctx.setAttribute(pp + entry.getKey(), entry.getValue()); + } + } + } + } + } catch (SvcLogicException e) { + boolean shouldRetry = false; + if (e.getCause().getCause() instanceof SocketException) { + shouldRetry = true; + } + + log.error("Error sending the request: " + e.getMessage(), e); + String prefix = parseParam(paramMap, responsePrefix, false, null); + if (retryPolicy == null || !shouldRetry) { + setFailureResponseStatus(ctx, prefix, e.getMessage(), r); + } else { + log.debug(retryPolicy.getRetryMessage()); + try { + // calling getNextHostName increments the retry count so it should be called before shouldRetry + String retryString = retryPolicy.getNextHostName(); + if (retryPolicy.shouldRetry()) { + paramMap.put(restapiUrlString, retryString); + log.debug("retry attempt {} will use the retry url {}", retryPolicy.getRetryCount(), + retryString); + sendRequest(paramMap, ctx, retryPolicy); + } else { + log.debug("Maximum retries reached, won't attempt to retry. Calling setFailureResponseStatus."); + setFailureResponseStatus(ctx, prefix, e.getMessage(), r); + } + } catch (Exception ex) { + String retryErrorMessage = "Retry attempt " + retryPolicy.getRetryCount() + + "has failed with error message " + ex.getMessage(); + setFailureResponseStatus(ctx, prefix, retryErrorMessage, r); + } + } + } + + if (r != null && r.code >= 300) { + throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message); + } + } + + protected void handlePartner(Map<String, String> paramMap) { + String partner = paramMap.get("partner"); + if (partner != null && partner.length() > 0) { + PartnerDetails details = partnerStore.get(partner); + paramMap.put(restapiUserKey, details.username); + paramMap.put(restapiPasswordKey, details.password); + if (paramMap.get(restapiUrlString) == null) { + paramMap.put(restapiUrlString, details.url); + } + } + } + + protected String buildXmlJsonRequest(SvcLogicContext ctx, String template, Format format) throws SvcLogicException { + log.info("Building {} started", format); + long t1 = System.currentTimeMillis(); + String originalTemplate = template; + + template = expandRepeats(ctx, template, 1); + + Map<String, String> mm = new HashMap<>(); + for (String s : ctx.getAttributeKeySet()) { + mm.put(s, ctx.getAttribute(s)); + } + + StringBuilder ss = new StringBuilder(); + int i = 0; + while (i < template.length()) { + int i1 = template.indexOf("${", i); + if (i1 < 0) { + ss.append(template.substring(i)); + break; + } + + int i2 = template.indexOf('}', i1 + 2); + if (i2 < 0) { + throw new SvcLogicException("Template error: Matching } not found"); + } + + String var1 = template.substring(i1 + 2, i2); + String value1 = format == Format.XML ? XmlJsonUtil.getXml(mm, var1) : XmlJsonUtil.getJson(mm, var1); + if (value1 == null || value1.trim().length() == 0) { + // delete the whole element (line) + int i3 = template.lastIndexOf('\n', i1); + if (i3 < 0) { + i3 = 0; + } + int i4 = template.indexOf('\n', i1); + if (i4 < 0) { + i4 = template.length(); + } + + if (i < i3) { + ss.append(template.substring(i, i3)); + } + i = i4; + } else { + ss.append(template.substring(i, i1)).append(value1); + i = i2 + 1; + } + } + + String req = format == Format.XML ? XmlJsonUtil.removeEmptyStructXml(ss.toString()) + : XmlJsonUtil.removeEmptyStructJson(originalTemplate, ss.toString()); + + if (format == Format.JSON) { + req = XmlJsonUtil.removeLastCommaJson(req); + } + + long t2 = System.currentTimeMillis(); + log.info("Building {} completed. Time: {}", format, t2 - t1); + + return req; + } + + protected String expandRepeats(SvcLogicContext ctx, String template, int level) throws SvcLogicException { + StringBuilder newTemplate = new StringBuilder(); + int k = 0; + while (k < template.length()) { + int i1 = template.indexOf("${repeat:", k); + if (i1 < 0) { + newTemplate.append(template.substring(k)); + break; + } + + int i2 = template.indexOf(':', i1 + 9); + if (i2 < 0) { + throw new SvcLogicException( + "Template error: Context variable name followed by : is required after repeat"); + } + + // Find the closing }, store in i3 + int nn = 1; + int i3 = -1; + int i = i2; + while (nn > 0 && i < template.length()) { + i3 = template.indexOf('}', i); + if (i3 < 0) { + throw new SvcLogicException("Template error: Matching } not found"); + } + int i32 = template.indexOf('{', i); + if (i32 >= 0 && i32 < i3) { + nn++; + i = i32 + 1; + } else { + nn--; + i = i3 + 1; + } + } + + String var1 = template.substring(i1 + 9, i2); + String value1 = ctx.getAttribute(var1); + log.info(" {}:{}", var1, value1); + int n = 0; + try { + n = Integer.parseInt(value1); + } catch (NumberFormatException e) { + log.info("value1 not set or not a number, n will remain set at zero"); + } + + newTemplate.append(template.substring(k, i1)); + + String rpt = template.substring(i2 + 1, i3); + + for (int ii = 0; ii < n; ii++) { + String ss = rpt.replaceAll("\\[\\$\\{" + level + "\\}\\]", "[" + ii + "]"); + if (ii == n - 1 && ss.trim().endsWith(",")) { + int i4 = ss.lastIndexOf(','); + if (i4 > 0) { + ss = ss.substring(0, i4) + ss.substring(i4 + 1); + } + } + newTemplate.append(ss); + } + + k = i3 + 1; + } + + if (k == 0) { + return newTemplate.toString(); + } + + return expandRepeats(ctx, newTemplate.toString(), level + 1); + } + + protected String readFile(String fileName) throws SvcLogicException { + try { + byte[] encoded = Files.readAllBytes(Paths.get(fileName)); + return new String(encoded, "UTF-8"); + } catch (IOException | SecurityException e) { + throw new SvcLogicException("Unable to read file " + fileName + e.getLocalizedMessage(), e); + } + } + + protected Client addAuthType(Client c, FileParam fp) throws SvcLogicException { + Parameters p = new Parameters(); + p.restapiUser = fp.user; + p.restapiPassword = fp.password; + p.oAuthConsumerKey = fp.oAuthConsumerKey; + p.oAuthVersion = fp.oAuthVersion; + p.oAuthConsumerSecret = fp.oAuthConsumerSecret; + p.oAuthSignatureMethod = fp.oAuthSignatureMethod; + p.authtype = fp.authtype; + return addAuthType(c, p); + } + + public Client addAuthType(Client client, Parameters p) throws SvcLogicException { + if (p.authtype == AuthType.Unspecified) { + if (p.restapiUser != null && p.restapiPassword != null) { + client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword)); + } else if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) { + Feature oAuth1Feature = + OAuth1ClientSupport.builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret)) + .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build(); + client.register(oAuth1Feature); + + } + } else { + if (p.authtype == AuthType.DIGEST) { + if (p.restapiUser != null && p.restapiPassword != null) { + client.register(HttpAuthenticationFeature.digest(p.restapiUser, p.restapiPassword)); + } else { + throw new SvcLogicException( + "oAUTH authentication type selected but all restapiUser and restapiPassword " + + "parameters doesn't exist", + new Throwable()); + } + } else if (p.authtype == AuthType.BASIC) { + if (p.restapiUser != null && p.restapiPassword != null) { + client.register(HttpAuthenticationFeature.basic(p.restapiUser, p.restapiPassword)); + } else { + throw new SvcLogicException( + "oAUTH authentication type selected but all restapiUser and restapiPassword " + + "parameters doesn't exist", + new Throwable()); + } + } else if (p.authtype == AuthType.OAUTH) { + if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) { + Feature oAuth1Feature = OAuth1ClientSupport + .builder(new ConsumerCredentials(p.oAuthConsumerKey, p.oAuthConsumerSecret)) + .version(p.oAuthVersion).signatureMethod(p.oAuthSignatureMethod).feature().build(); + client.register(oAuth1Feature); + } else { + throw new SvcLogicException( + "oAUTH authentication type selected but all oAuthConsumerKey, oAuthConsumerSecret " + + "and oAuthSignatureMethod parameters doesn't exist", + new Throwable()); + } + } + } + return client; + } + + /** + * Receives the http response for the http request sent. + * + * @param request request msg + * @param p parameters + * @return HTTP response + * @throws SvcLogicException when sending http request fails + */ + public HttpResponse sendHttpRequest(String request, Parameters p) throws SvcLogicException { + + SSLContext ssl = null; + if (p.ssl && p.restapiUrl.startsWith("https")) { + ssl = createSSLContext(p); + } + Client client; + if (ssl != null) { + HttpsURLConnection.setDefaultSSLSocketFactory(ssl.getSocketFactory()); + client = ClientBuilder.newBuilder().sslContext(ssl).hostnameVerifier((s, sslSession) -> true).build(); + } else { + client = ClientBuilder.newBuilder().hostnameVerifier((s, sslSession) -> true).build(); + } + + setClientTimeouts(client); + // Needed to support additional HTTP methods such as PATCH + client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); + client.register(new MetricLogClientFilter()); + WebTarget webTarget = addAuthType(client, p).target(p.restapiUrl); + + long t1 = System.currentTimeMillis(); + + HttpResponse r = new HttpResponse(); + r.code = 200; + String accept = p.accept; + if (accept == null) { + accept = p.format == Format.XML ? "application/xml" : "application/json"; + } + + String contentType = p.contentType; + if (contentType == null) { + contentType = accept + ";charset=UTF-8"; + } + + if (!p.skipSending && !p.multipartFormData) { + + Invocation.Builder invocationBuilder = webTarget.request(contentType).accept(accept); + + if (p.format == Format.NONE) { + invocationBuilder.header("", ""); + } + + if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) { + String[] keyValuePairs = p.customHttpHeaders.split(","); + for (String singlePair : keyValuePairs) { + int equalPosition = singlePair.indexOf('='); + invocationBuilder.header(singlePair.substring(0, equalPosition), + singlePair.substring(equalPosition + 1, singlePair.length())); + } + } + + invocationBuilder.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); + + Response response; + + try { + // When the HTTP operation has no body do not set the content-type + //setting content-type has caused errors with some servers when no body is present + if (request == null) { + response = invocationBuilder.method(p.httpMethod.toString()); + } else { + log.info("Sending request below to url " + p.restapiUrl); + log.info(request); + response = invocationBuilder.method(p.httpMethod.toString(), entity(request, contentType)); + } + } catch (ProcessingException | IllegalStateException e) { + throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); + } + + r.code = response.getStatus(); + r.headers = response.getStringHeaders(); + EntityTag etag = response.getEntityTag(); + if (etag != null) { + r.message = etag.getValue(); + } + if (response.hasEntity() && r.code != 204) { + r.body = response.readEntity(String.class); + } + } else if (!p.skipSending && p.multipartFormData) { + + WebTarget wt = client.register(MultiPartFeature.class).target(p.restapiUrl); + + MultiPart multiPart = new MultiPart(); + multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE); + + FileDataBodyPart fileDataBodyPart = + new FileDataBodyPart("file", new File(p.multipartFile), MediaType.APPLICATION_OCTET_STREAM_TYPE); + multiPart.bodyPart(fileDataBodyPart); + + + Invocation.Builder invocationBuilder = wt.request(contentType).accept(accept); + + if (p.format == Format.NONE) { + invocationBuilder.header("", ""); + } + + if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) { + String[] keyValuePairs = p.customHttpHeaders.split(","); + for (String singlePair : keyValuePairs) { + int equalPosition = singlePair.indexOf('='); + invocationBuilder.header(singlePair.substring(0, equalPosition), + singlePair.substring(equalPosition + 1, singlePair.length())); + } + } + + Response response; + + try { + response = + invocationBuilder.method(p.httpMethod.toString(), entity(multiPart, multiPart.getMediaType())); + } catch (ProcessingException | IllegalStateException e) { + throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); + } + + r.code = response.getStatus(); + r.headers = response.getStringHeaders(); + EntityTag etag = response.getEntityTag(); + if (etag != null) { + r.message = etag.getValue(); + } + if (response.hasEntity() && r.code != 204) { + r.body = response.readEntity(String.class); + } + + } + + long t2 = System.currentTimeMillis(); + log.info(responseReceivedMessage, t2 - t1); + log.info(responseHttpCodeMessage, r.code); + log.info("HTTP response message: {}", r.message); + logHeaders(r.headers); + log.info("HTTP response: {}", r.body); + + return r; + } + + protected SSLContext createSSLContext(Parameters p) { + try (FileInputStream in = new FileInputStream(p.keyStoreFileName)) { + HttpsURLConnection.setDefaultHostnameVerifier((string, ssls) -> true); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + KeyStore ks = KeyStore.getInstance("PKCS12"); + char[] pwd = p.keyStorePassword.toCharArray(); + ks.load(in, pwd); + kmf.init(ks, pwd); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(kmf.getKeyManagers(), null, null); + return ctx; + } catch (Exception e) { + log.error("Error creating SSLContext: {}", e.getMessage(), e); + } + return null; + } + + protected void setFailureResponseStatus(SvcLogicContext ctx, String prefix, String errorMessage, + HttpResponse resp) { + resp.code = 500; + resp.message = errorMessage; + String pp = prefix != null ? prefix + '.' : ""; + ctx.setAttribute(pp + "response-code", String.valueOf(resp.code)); + ctx.setAttribute(pp + "response-message", resp.message); + } + + protected void setResponseStatus(SvcLogicContext ctx, String prefix, HttpResponse r) { + String pp = prefix != null ? prefix + '.' : ""; + ctx.setAttribute(pp + "response-code", String.valueOf(r.code)); + ctx.setAttribute(pp + "response-message", r.message); + } + + public void sendFile(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { + HttpResponse r = null; + try { + FileParam p = getFileParameters(paramMap); + byte[] data = Files.readAllBytes(Paths.get(p.fileName)); + + r = sendHttpData(data, p); + + for (int i = 0; i < 10 && r.code == 301; i++) { + String newUrl = r.headers2.get("Location").get(0); + + log.info("Got response code 301. Sending same request to URL: " + newUrl); + + p.url = newUrl; + r = sendHttpData(data, p); + } + + setResponseStatus(ctx, p.responsePrefix, r); + + } catch (SvcLogicException | IOException e) { + log.error("Error sending the request: {}", e.getMessage(), e); + + r = new HttpResponse(); + r.code = 500; + r.message = e.getMessage(); + String prefix = parseParam(paramMap, responsePrefix, false, null); + setResponseStatus(ctx, prefix, r); + } + + if (r != null && r.code >= 300) { + throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message); + } + } + + private FileParam getFileParameters(Map<String, String> paramMap) throws SvcLogicException { + FileParam p = new FileParam(); + p.fileName = parseParam(paramMap, "fileName", true, null); + p.url = parseParam(paramMap, "url", true, null); + p.user = parseParam(paramMap, "user", false, null); + p.password = parseParam(paramMap, "password", false, null); + p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post")); + p.responsePrefix = parseParam(paramMap, responsePrefix, false, null); + String skipSendingStr = paramMap.get(skipSendingMessage); + p.skipSending = "true".equalsIgnoreCase(skipSendingStr); + p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null); + p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null); + p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null); + p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null); + p.authtype = fromString(parseParam(paramMap, "authType", false, "unspecified")); + return p; + } + + public void postMessageOnUeb(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException { + HttpResponse r; + try { + UebParam p = getUebParameters(paramMap); + + String pp = p.responsePrefix != null ? p.responsePrefix + '.' : ""; + + String req; + + if (p.templateFileName == null) { + log.info("No template file name specified. Using default UEB template: {}", defaultUebTemplateFileName); + p.templateFileName = defaultUebTemplateFileName; + } + + String reqTemplate = readFile(p.templateFileName); + reqTemplate = reqTemplate.replaceAll("rootVarName", p.rootVarName); + req = buildXmlJsonRequest(ctx, reqTemplate, Format.JSON); + + r = postOnUeb(req, p); + setResponseStatus(ctx, p.responsePrefix, r); + if (r.body != null) { + ctx.setAttribute(pp + "httpResponse", r.body); + } + + } catch (SvcLogicException e) { + log.error("Error sending the request: {}", e.getMessage(), e); + + r = new HttpResponse(); + r.code = 500; + r.message = e.getMessage(); + String prefix = parseParam(paramMap, responsePrefix, false, null); + setResponseStatus(ctx, prefix, r); + } + + if (r.code >= 300) { + throw new SvcLogicException(String.valueOf(r.code) + ": " + r.message); + } + } + + protected HttpResponse sendHttpData(byte[] data, FileParam p) throws IOException { + URL url = new URL(p.url); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + + log.info("Connection: " + con.getClass().getName()); + + con.setRequestMethod(p.httpMethod.toString()); + con.setRequestProperty("Content-Type", "application/octet-stream"); + con.setRequestProperty("Accept", "*/*"); + con.setRequestProperty("Expect", "100-continue"); + con.setFixedLengthStreamingMode(data.length); + con.setInstanceFollowRedirects(false); + + if (p.user != null && p.password != null) { + String authString = p.user + ":" + p.password; + String authStringEnc = Base64.getEncoder().encodeToString(authString.getBytes()); + con.setRequestProperty("Authorization", "Basic " + authStringEnc); + } + + con.setDoInput(true); + con.setDoOutput(true); + + log.info("Sending file"); + long t1 = System.currentTimeMillis(); + + HttpResponse r = new HttpResponse(); + r.code = 200; + + if (!p.skipSending) { + HttpURLConnectionMetricUtil util = new HttpURLConnectionMetricUtil(); + util.logBefore(con, ONAPComponents.DMAAP); + + con.connect(); + + boolean continue100failed = false; + try { + OutputStream os = con.getOutputStream(); + os.write(data); + os.flush(); + os.close(); + } catch (ProtocolException e) { + continue100failed = true; + } + + r.code = con.getResponseCode(); + r.headers2 = con.getHeaderFields(); + + if (r.code != 204 && !continue100failed) { + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + r.body = response.toString(); + } + + util.logAfter(con); + + con.disconnect(); + } + + long t2 = System.currentTimeMillis(); + log.info("Response received. Time: {}", t2 - t1); + log.info("HTTP response code: {}", r.code); + log.info("HTTP response message: {}", r.message); + logHeaders(r.headers2); + log.info("HTTP response: {}", r.body); + + return r; + } + + private UebParam getUebParameters(Map<String, String> paramMap) throws SvcLogicException { + UebParam p = new UebParam(); + p.topic = parseParam(paramMap, "topic", true, null); + p.templateFileName = parseParam(paramMap, "templateFileName", false, null); + p.rootVarName = parseParam(paramMap, "rootVarName", false, null); + p.responsePrefix = parseParam(paramMap, responsePrefix, false, null); + String skipSendingStr = paramMap.get(skipSendingMessage); + p.skipSending = "true".equalsIgnoreCase(skipSendingStr); + return p; + } + + protected void logProperties(Map<String, Object> mm) { + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) { + ll.add((String) o); + } + Collections.sort(ll); + + log.info("Properties:"); + for (String name : ll) { + log.info("--- {}:{}", name, String.valueOf(mm.get(name))); + } + } + + protected void logHeaders(MultivaluedMap<String, String> mm) { + log.info("HTTP response headers:"); + + if (mm == null) { + return; + } + + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) { + ll.add((String) o); + } + Collections.sort(ll); + + for (String name : ll) { + log.info("--- {}:{}", name, String.valueOf(mm.get(name))); + } + } + + private void logHeaders(Map<String, List<String>> mm) { + if (mm == null || mm.isEmpty()) { + return; + } + + List<String> ll = new ArrayList<>(); + for (String s : mm.keySet()) { + if (s != null) { + ll.add(s); + } + } + Collections.sort(ll); + + for (String name : ll) { + List<String> v = mm.get(name); + log.info("--- {}:{}", name, String.valueOf(mm.get(name))); + log.info("--- " + name + ": " + (v.size() == 1 ? v.get(0) : v)); + } + } + + protected HttpResponse postOnUeb(String request, UebParam p) throws SvcLogicException { + String[] urls = uebServers.split(" "); + for (int i = 0; i < urls.length; i++) { + if (!urls[i].endsWith("/")) { + urls[i] += "/"; + } + urls[i] += "events/" + p.topic; + } + + Client client = ClientBuilder.newBuilder().build(); + setClientTimeouts(client); + WebTarget webTarget = client.target(urls[0]); + + log.info("UEB URL: {}", urls[0]); + log.info("Sending request:"); + log.info(request); + long t1 = System.currentTimeMillis(); + + HttpResponse r = new HttpResponse(); + r.code = 200; + + if (!p.skipSending) { + String tt = "application/json"; + String tt1 = tt + ";charset=UTF-8"; + + Response response; + Invocation.Builder invocationBuilder = webTarget.request(tt1).accept(tt); + + try { + response = invocationBuilder.post(Entity.entity(request, tt1)); + } catch (ProcessingException e) { + throw new SvcLogicException(requestPostingException + e.getLocalizedMessage(), e); + } + r.code = response.getStatus(); + r.headers = response.getStringHeaders(); + if (response.hasEntity()) { + r.body = response.readEntity(String.class); + } + } + + long t2 = System.currentTimeMillis(); + log.info(responseReceivedMessage, t2 - t1); + log.info(responseHttpCodeMessage, r.code); + logHeaders(r.headers); + log.info("HTTP response:\n {}", r.body); + + return r; + } + + public void setUebServers(String uebServers) { + this.uebServers = uebServers; + } + + public void setDefaultUebTemplateFileName(String defaultUebTemplateFileName) { + this.defaultUebTemplateFileName = defaultUebTemplateFileName; + } + + protected void setClientTimeouts(Client client) { + client.property(ClientProperties.CONNECT_TIMEOUT, httpConnectTimeout); + client.property(ClientProperties.READ_TIMEOUT, httpReadTimeout); + } + + protected Integer readOptionalInteger(String propertyName, Integer defaultValue) { + String stringValue = System.getProperty(propertyName); + if (stringValue != null && stringValue.length() > 0) { + try { + return Integer.valueOf(stringValue); + } catch (NumberFormatException e) { + log.warn("property " + propertyName + " had the value " + stringValue + " that could not be converted to an Integer, default " + defaultValue + " will be used instead", e); + } + } + return defaultValue; + } + + protected static String[] getMultipleUrls(String restapiUrl) { + List<String> urls = new ArrayList<>(); + int start = 0; + for (int i = 0; i < restapiUrl.length(); i++) { + if (restapiUrl.charAt(i) == ',') { + if (i + 9 < restapiUrl.length()) { + String part = restapiUrl.substring(i + 1, i + 9); + if (part.equals("https://") || part.startsWith("http://")) { + urls.add(restapiUrl.substring(start, i)); + start = i + 1; + } + } + } else if (i == restapiUrl.length() - 1) { + urls.add(restapiUrl.substring(start, i + 1)); + } + } + String[] arr = new String[urls.size()]; + return urls.toArray(arr); + } + + protected static boolean containsMultipleUrls(String restapiUrl) { + Matcher m = retryPattern.matcher(restapiUrl); + return m.matches(); + } + + private static class FileParam { + + public String fileName; + public String url; + public String user; + public String password; + public HttpMethod httpMethod; + public String responsePrefix; + public boolean skipSending; + public String oAuthConsumerKey; + public String oAuthConsumerSecret; + public String oAuthSignatureMethod; + public String oAuthVersion; + public AuthType authtype; + } + + private static class UebParam { + + public String topic; + public String templateFileName; + public String rootVarName; + public String responsePrefix; + public boolean skipSending; + } +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryException.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryException.java new file mode 100644 index 000000000..03078c652 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryException.java @@ -0,0 +1,30 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +public class RetryException extends Exception { + + public RetryException(String message) { + super(message); + } + +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryPolicy.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryPolicy.java new file mode 100644 index 000000000..8d5143be3 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/RetryPolicy.java @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +public class RetryPolicy { + private String[] hostnames; + private Integer maximumRetries; + private int position; + private int retryCount; + + public RetryPolicy(String[] hostnames, Integer maximumRetries) { + this.hostnames = hostnames; + this.maximumRetries = maximumRetries; + this.position = 0; + this.retryCount = 0; + + } + + public Integer getMaximumRetries() { + return maximumRetries; + } + + public int getRetryCount() { + return retryCount; + } + + public Boolean shouldRetry() { + return retryCount < maximumRetries + 1; + } + + public String getRetryMessage() { + return retryCount + " retry attempts were made out of " + maximumRetries + " maximum retry attempts."; + } + + public String getNextHostName() throws RetryException { + retryCount++; + position++; + if (position > hostnames.length - 1) { + position = 0; + } + return hostnames[position]; + } + +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlJsonUtil.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlJsonUtil.java new file mode 100644 index 000000000..9870221d7 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlJsonUtil.java @@ -0,0 +1,518 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.text.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class XmlJsonUtil { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(XmlJsonUtil.class); + + private XmlJsonUtil() { + // Preventing instantiation of the same. + } + + public static String getXml(Map<String, String> varmap, String var) { + boolean escape = true; + if (var.startsWith("'")) { + var = var.substring(1); + escape = false; + } + + Object o = createStructure(varmap, var); + return generateXml(o, 0, escape); + } + + public static String getJson(Map<String, String> varmap, String var) { + boolean escape = true; + if (var.startsWith("'")) { + var = var.substring(1); + escape = false; + } + + boolean quotes = true; + if (var.startsWith("\"")) { + var = var.substring(1); + quotes = false; + } + + Object o = createStructure(varmap, var); + return generateJson(o, escape, quotes); + } + + private static Object createStructure(Map<String, String> flatmap, String var) { + if (flatmap.containsKey(var)) { + return flatmap.get(var); + } + + Map<String, Object> mm = new HashMap<>(); + List<Object> ll = new ArrayList<>(); + + for (Map.Entry<String, String> e : flatmap.entrySet()) { + String key = e.getKey(); + String value = e.getValue(); + + if (key.endsWith("_length") || key.endsWith("].key")) { + continue; + } + + if (value == null || value.trim().isEmpty()) { + continue; + } + + if (key.startsWith(var + "[")) { + String newKey = key.substring(var.length()); + set(ll, newKey, value); + } else if (var == null || var.isEmpty()) { + set(mm, key, value); + } else if (key.startsWith(var + ".")) { + String newKey = key.substring(var.length() + 1); + set(mm, newKey, value); + } + } + + if (!mm.isEmpty()) { + return mm; + } + if (!ll.isEmpty()) { + return ll; + } + return null; + } + + @SuppressWarnings("unchecked") + public static void set(Object struct, String compositeKey, Object value) { + if (struct == null) { + throw new IllegalArgumentException("Null argument: struct"); + } + + if (compositeKey == null || compositeKey.length() == 0) { + throw new IllegalArgumentException("Null or empty argument: compositeKey"); + } + + if (value == null) { + return; + } + + List<Object> keys = splitCompositeKey(compositeKey); + Object currentValue = struct; + String currentKey = ""; + + for (int i = 0; i < keys.size() - 1; i++) { + Object key = keys.get(i); + + if (key instanceof Integer) { + if (!(currentValue instanceof List)) { + throw new IllegalArgumentException("Cannot resolve: " + compositeKey + ": References list '" + currentKey + "', but '" + currentKey + "' is not a list"); + } + + Integer keyi = (Integer) key; + List<Object> currentValueL = (List<Object>) currentValue; + int size = currentValueL.size(); + + if (keyi >= size) { + for (int k = 0; k < keyi - size + 1; k++) { + currentValueL.add(null); + } + } + + Object newValue = currentValueL.get(keyi); + if (newValue == null) { + Object nextKey = keys.get(i + 1); + if (nextKey instanceof Integer) { + newValue = new ArrayList<>(); + } else { + newValue = new HashMap<>(); + } + currentValueL.set(keyi, newValue); + } + + currentValue = newValue; + currentKey += "[" + key + "]"; + + } else { + if (!(currentValue instanceof Map)) { + throw new IllegalArgumentException("Cannot resolve: " + compositeKey + ": References map '" + currentKey + "', but '" + currentKey + "' is not a map"); + } + + Object newValue = ((Map<String, Object>) currentValue).get(key); + if (newValue == null) { + Object nextKey = keys.get(i + 1); + if (nextKey instanceof Integer) { + newValue = new ArrayList<>(); + } else { + newValue = new HashMap<>(); + } + ((Map<String, Object>) currentValue).put((String) key, newValue); + } + + currentValue = newValue; + currentKey += "." + key; + } + } + + Object key = keys.get(keys.size() - 1); + if (key instanceof Integer) { + if (!(currentValue instanceof List)) { + throw new IllegalArgumentException("Cannot resolve: " + compositeKey + ": References list '" + currentKey + "', but '" + currentKey + "' is not a list"); + } + + Integer keyi = (Integer) key; + List<Object> currentValueL = (List<Object>) currentValue; + int size = currentValueL.size(); + + if (keyi >= size) { + for (int k = 0; k < keyi - size + 1; k++) { + currentValueL.add(null); + } + } + + currentValueL.set(keyi, value); + + } else { + if (!(currentValue instanceof Map)) { + throw new IllegalArgumentException("Cannot resolve: " + compositeKey + ": References map '" + currentKey + "', but '" + currentKey + "' is not a map"); + } + + ((Map<String, Object>) currentValue).put((String) key, value); + } + } + + private static List<Object> splitCompositeKey(String compositeKey) { + if (compositeKey == null) { + return Collections.emptyList(); + } + + String[] ss = compositeKey.split("\\."); + List<Object> ll = new ArrayList<>(); + for (String s : ss) { + if (s.length() == 0) { + continue; + } + + int i1 = s.indexOf('['); + if (i1 < 0) { + ll.add(s); + } else { + if (!s.endsWith("]")) { + throw new IllegalArgumentException("Invalid composite key: " + compositeKey + ": No matching ] found"); + } + + String s1 = s.substring(0, i1); + if (s1.length() > 0) { + ll.add(s1); + } + + String s2 = s.substring(i1 + 1, s.length() - 1); + try { + int n = Integer.parseInt(s2); + if (n < 0) { + throw new IllegalArgumentException("Invalid composite key: " + compositeKey + ": Index must be >= 0: " + n); + } + + ll.add(n); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid composite key: " + compositeKey + ": Index not a number: " + s2); + } + } + } + + return ll; + } + + @SuppressWarnings("unchecked") + private static String generateXml(Object o, int indent, boolean escape) { + if (o == null) { + return null; + } + + if (o instanceof String) { + return escape ? StringEscapeUtils.escapeXml10((String) o) : (String) o; + }; + + if (o instanceof Map) { + StringBuilder ss = new StringBuilder(); + Map<String, Object> mm = (Map<String, Object>) o; + for (Map.Entry<String, Object> entry : mm.entrySet()) { + Object v = entry.getValue(); + String key = entry.getKey(); + if (v instanceof String) { + String s = escape ? StringEscapeUtils.escapeXml10((String) v) : (String) v; + ss.append(pad(indent)).append('<').append(key).append('>'); + ss.append(s); + ss.append("</").append(key).append('>').append('\n'); + } else if (v instanceof Map) { + ss.append(pad(indent)).append('<').append(key).append('>').append('\n'); + ss.append(generateXml(v, indent + 1, escape)); + ss.append(pad(indent)).append("</").append(key).append('>').append('\n'); + } else if (v instanceof List) { + List<Object> ll = (List<Object>) v; + for (Object o1 : ll) { + ss.append(pad(indent)).append('<').append(key).append('>').append('\n'); + ss.append(generateXml(o1, indent + 1, escape)); + ss.append(pad(indent)).append("</").append(key).append('>').append('\n'); + } + } + } + return ss.toString(); + } + + return null; + } + private static String generateJson(Object o, boolean escape, boolean quotes) { + if (o == null) { + return null; + } + if (o instanceof String && ((String) o).length() == 0) { + return null; + } + + StringBuilder ss = new StringBuilder(); + generateJson(ss, o, 0, false, escape, quotes); + return ss.toString(); + } + + @SuppressWarnings("unchecked") + private static void generateJson(StringBuilder ss, Object o, int indent, boolean padFirst, boolean escape, boolean quotes) { + if (o instanceof String) { + String s = escape ? StringEscapeUtils.escapeJson((String) o) : (String) o; + if (padFirst) { + ss.append(pad(indent)); + } + if (quotes) { + ss.append('"').append(s).append('"'); + } else { + ss.append(s); + } + return; + } + + if (o instanceof Map) { + Map<String, Object> mm = (Map<String, Object>) o; + + if (padFirst) { + ss.append(pad(indent)); + } + ss.append("{\n"); + + boolean first = true; + for (Map.Entry<String, Object> entry : mm.entrySet()) { + if (!first) { + ss.append(",\n"); + } + first = false; + Object v = entry.getValue(); + String key = entry.getKey(); + ss.append(pad(indent + 1)).append('"').append(key).append("\": "); + generateJson(ss, v, indent + 1, false, escape, true); + } + + ss.append("\n"); + ss.append(pad(indent)).append('}'); + + return; + } + + if (o instanceof List) { + List<Object> ll = (List<Object>) o; + + if (padFirst) { + ss.append(pad(indent)); + } + ss.append("[\n"); + + boolean first = true; + for (Object o1 : ll) { + if (!first) { + ss.append(",\n"); + } + first = false; + + generateJson(ss, o1, indent + 1, true, escape, quotes); + } + + ss.append("\n"); + ss.append(pad(indent)).append(']'); + } + } + + public static String removeLastCommaJson(String s) { + StringBuilder sb = new StringBuilder(); + int k = 0; + int start = 0; + while (k < s.length()) { + int i11 = s.indexOf('}', k); + int i12 = s.indexOf(']', k); + int i1 = -1; + if (i11 < 0) { + i1 = i12; + } else if (i12 < 0) { + i1 = i11; + } else { + i1 = i11 < i12 ? i11 : i12; + } + if (i1 < 0) { + break; + } + + int i2 = s.lastIndexOf(',', i1); + if (i2 < 0) { + k = i1 + 1; + continue; + } + + String between = s.substring(i2 + 1, i1); + if (between.trim().length() > 0) { + k = i1 + 1; + continue; + } + + sb.append(s.substring(start, i2)); + start = i2 + 1; + k = i1 + 1; + } + + sb.append(s.substring(start, s.length())); + + return sb.toString(); + } + + public static String removeEmptyStructJson(String template, String s) { + int k = 0; + while (k < s.length()) { + boolean curly = true; + int i11 = s.indexOf('{', k); + int i12 = s.indexOf('[', k); + int i1 = -1; + if (i11 < 0) { + i1 = i12; + curly = false; + } else if (i12 < 0) { + i1 = i11; + } else if (i11 < i12) { + i1 = i11; + } else { + i1 = i12; + curly = false; + } + + if (i1 >= 0) { + int i2 = curly ? s.indexOf('}', i1) : s.indexOf(']', i1); + if (i2 > 0) { + String value = s.substring(i1 + 1, i2); + if (value.trim().length() == 0) { + int i4 = s.lastIndexOf('\n', i1); + if (i4 < 0) { + i4 = 0; + } + int i5 = s.indexOf('\n', i2); + if (i5 < 0) { + i5 = s.length(); + } + + + /*If template mandates empty construct to be present, those should not be removed.*/ + if (template != null && template.contains(s.substring(i4))) { + k = i1 + 1; + } else { + s = s.substring(0, i4) + s.substring(i5); + k = 0; + } + } else { + k = i1 + 1; + } + } else { + break; + } + } else { + break; + } + } + + return s; + } + + public static String removeEmptyStructXml(String s) { + int k = 0; + while (k < s.length()) { + int i1 = s.indexOf('<', k); + if (i1 < 0 || i1 == s.length() - 1) { + break; + } + + char c1 = s.charAt(i1 + 1); + if (c1 == '?' || c1 == '!') { + k = i1 + 2; + continue; + } + + int i2 = s.indexOf('>', i1); + if (i2 < 0) { + k = i1 + 1; + continue; + } + + String closingTag = "</" + s.substring(i1 + 1, i2 + 1); + int i3 = s.indexOf(closingTag, i2 + 1); + if (i3 < 0) { + k = i2 + 1; + continue; + } + + String value = s.substring(i2 + 1, i3); + if (value.trim().length() > 0) { + k = i2 + 1; + continue; + } + + int i4 = s.lastIndexOf('\n', i1); + if (i4 < 0) { + i4 = 0; + } + int i5 = s.indexOf('\n', i3); + if (i5 < 0) { + i5 = s.length(); + } + + s = s.substring(0, i4) + s.substring(i5); + k = 0; + } + + return s; + } + + private static String pad(int n) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < n; i++) { + s.append(Character.toString('\t')); + } + return s.toString(); + } +} diff --git a/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlParser.java b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlParser.java new file mode 100644 index 000000000..42e9e57ad --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/restapicall/XmlParser.java @@ -0,0 +1,180 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.XMLConstants; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.XMLReader; + +public final class XmlParser { + + private static final Logger log = LoggerFactory.getLogger(XmlParser.class); + + private XmlParser() { + // Preventing instantiation of the same. + } + + public static Map<String, String> convertToProperties(String s, Set<String> listNameList) + throws SvcLogicException { + + checkNotNull(s, "Input should not be null."); + + Handler handler = new Handler(listNameList); + try { + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + spf.setFeature("http://xml.org/sax/features/external-general-entities", false); + spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + InputStream in = new ByteArrayInputStream(s.getBytes()); + SAXParser saxParser = spf.newSAXParser(); + saxParser.parse(in, handler); + + } catch (ParserConfigurationException | IOException | SAXException | NumberFormatException e) { + throw new SvcLogicException("Unable to convert XML to properties" + e.getLocalizedMessage(), e); + } + return handler.getProperties(); + } + + private static class Handler extends DefaultHandler { + + private Set<String> listNameList; + + private Map<String, String> properties = new HashMap<>(); + + StringBuilder currentName = new StringBuilder(); + StringBuilder currentValue = new StringBuilder(); + + public Handler(Set<String> listNameList) { + super(); + this.listNameList = listNameList; + if (this.listNameList == null) + this.listNameList = new HashSet<>(); + } + + public Map<String, String> getProperties() { + return properties; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + super.startElement(uri, localName, qName, attributes); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + if (currentName.length() > 0) + currentName.append(Character.toString('.')); + currentName.append(name); + + String listName = removeIndexes(currentName.toString()); + + if (listNameList.contains(listName)) { + String n = currentName.toString() + "_length"; + int len = getInt(properties, n); + properties.put(n, String.valueOf(len + 1)); + currentName.append("[").append(len).append("]"); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + String s = currentValue.toString().trim(); + if (s.length() > 0) { + properties.put(currentName.toString(), s); + + log.info("Added property: {} : {}", currentName, s); + currentValue = new StringBuilder(); + } + + int i1 = currentName.lastIndexOf("." + name); + if (i1 <= 0) + currentName = new StringBuilder(); + else + currentName = new StringBuilder(currentName.substring(0, i1)); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + + String value = new String(ch, start, length); + currentValue.append(value); + } + + private static int getInt(Map<String, String> mm, String name) { + String s = mm.get(name); + if (s == null) + return 0; + return Integer.parseInt(s); + } + + private String removeIndexes(String currentName) { + StringBuilder b = new StringBuilder(); + boolean add = true; + for (int i = 0; i < currentName.length(); i++) { + char c = currentName.charAt(i); + if (c == '[') + add = false; + else if (c == ']') + add = true; + else if (add) + b.append(Character.toString(c)); + } + return b.toString(); + } + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/restapi-call-node-blueprint.xml b/plugins/restapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/restapi-call-node-blueprint.xml new file mode 100755 index 000000000..390bb0a35 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/restapi-call-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="restapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode" /> + + + + <service ref="restapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/main/resources/actokentemplate.json b/plugins/restapi-call-node/provider/src/main/resources/actokentemplate.json new file mode 100644 index 000000000..02e62400f --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/actokentemplate.json @@ -0,0 +1,4 @@ +{ + "userName": ${prop.sdncRestApi.thirdpartySdnc.user}, + "password": ${prop.sdncRestApi.thirdpartySdnc.password} +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/default-ueb-message.json b/plugins/restapi-call-node/provider/src/main/resources/default-ueb-message.json new file mode 100644 index 000000000..484d872f5 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/default-ueb-message.json @@ -0,0 +1,11 @@ +{ + "event":{ + "header":{ + "eventSource":"sdn-c" + }, + "body": + +${rootVarName} + + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/get-multicast-data.json b/plugins/restapi-call-node/provider/src/main/resources/get-multicast-data.json new file mode 100644 index 000000000..3b9997f64 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/get-multicast-data.json @@ -0,0 +1,13 @@ +{ + "input": { + "sdnc-request-header": { + "svc-request-id": ${connection-details-notification-input.configuration-response-common.svc-request-id}, + "svc-action": "updatemulticastvpn" + }, + "service-information": { + "service-type": "AVPN", + "service-instance-id": ${tmp.ete-vpn-key} + } + } +} + diff --git a/plugins/restapi-call-node/provider/src/main/resources/l2-dci-connects-template.json b/plugins/restapi-call-node/provider/src/main/resources/l2-dci-connects-template.json new file mode 100644 index 000000000..e0896bbaa --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/l2-dci-connects-template.json @@ -0,0 +1,11 @@ +{ + "l2-dci-connect": { + "id": ${prop.dci-connects.id}, + "name": ${prop.dci-connects.name}, + "description": ${prop.dci-connects.description}, + "network_id": ${prop.dci-connects.local_networks[0]}, + "evpn_irts": ${prop.dci-connects.evpn_irts}, + "evpn_erts": ${prop.dci-connects.evpn_erts}, + "vni": ${prop.dci-connects.vni} + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/l3-dci-connects-template.json b/plugins/restapi-call-node/provider/src/main/resources/l3-dci-connects-template.json new file mode 100644 index 000000000..41df794fd --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/l3-dci-connects-template.json @@ -0,0 +1,14 @@ +{ + "l3-dci-connect": { + "id": ${prop.dci-connects.id}, + "name": ${prop.dci-connects.name}, + "description": ${prop.dci-connects.description}, + "router_id": ${prop.dci-connects.router_id}, + "firewall_enable": false, + "local_networks": ${prop.dci-connects.local_networks}, + "local_network_all": false, + "evpn_irts": ${prop.dci-connects.evpn_irts}, + "evpn_erts": ${prop.dci-connects.evpn_erts}, + "l3_vni": ${prop.dci-connects.vni} + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/l3smsitetemplate.json b/plugins/restapi-call-node/provider/src/main/resources/l3smsitetemplate.json new file mode 100644 index 000000000..8f8156698 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/l3smsitetemplate.json @@ -0,0 +1,162 @@ +{ + "site":[ + { + "site-id": ${prop.l3vpn.site1_name}, + "vpn-policies": { + "vpn-policy": [ + { + "vpn-policy-id": ${prop.l3vpn.vpn-policy1-id}, + "entries": [ + { + "id": ${prop.l3vpn.entry1-id}, + "vpn": { + "vpn-id": ${prop.l3vpn.name}, + "site-role": "huawei-ac-net-l3vpn-svc:any-to-any-role" + } + } + ] + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": ${prop.l3vpn.sna1_name}, + "bearer": { + "appointed-access": { + "ne-id": ${prop.l3vpn.pe1_id}, + "ltp-id": ${prop.l3vpn.ac1_id}, + "vlan": { + "type": "huawei-ac-net-l3vpn-svc:vlan-dot1q", + "vlan-id": ${prop.l3vpn.sna1_svlan} + } + } + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "huawei-ac-net-l3vpn-svc:static-address", + "addresses": { + "provider-address": ${prop.l3vpn.ac1-peer-ip}, + "customer-address": ${prop.l3vpn.ac1-ip}, + "mask": "24" + } + } + }, + "service": { + "mtu": "1500" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": ${prop.l3vpn.ac1_protocol}, + "static": { + "cascaded-lan-prefixes": [ + { + "ipv4-lan-prefixes": [ + { + "ip-prefix": ${prop.l3vpn.sna1-route.ip-prefix}, + "next-hop": ${prop.l3vpn.sna1-route.next-hop} + } + ] + } + ] + } + "bgp": { + "peers": [ + { + "peer-ip": ${prop.l3vpn.peer1-ip}, + "remote-as": ${prop.l3vpn.ac1_protocol_bgp_as} + } + ] + } + } + } + ] + }, + "vpn-attachment": { + "vpn-policy-id": ${prop.l3vpn.name} + } + } + ] + }, + { + "site-id": ${prop.l3vpn.site2_name}, + "vpn-policies": { + "vpn-policy": [ + { + "vpn-policy-id": ${prop.l3vpn.vpn-policy2-id}, + "entries": [ + { + "id": ${prop.l3vpn.entry2-id}, + "vpn": { + "vpn-id": ${prop.l3vpn.name}, + "site-role": "huawei-ac-net-l3vpn-svc:any-to-any-role" + } + } + ] + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": ${prop.l3vpn.sna2_name}, + "bearer": { + "appointed-access": { + "ne-id": ${prop.l3vpn.pe2_id}, + "ltp-id": ${prop.l3vpn.ac2_id}, + "vlan": { + "type": "huawei-ac-net-l3vpn-svc:vlan-dot1q", + "vlan-id": ${prop.l3vpn.sna2_svlan} + } + } + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "huawei-ac-net-l3vpn-svc:static-address", + "addresses": { + "provider-address": ${prop.l3vpn.ac2-peer-ip}, + "customer-address": ${prop.l3vpn.ac2-ip}, + "mask": "24" + } + } + }, + "service": { + "mtu": "1500" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": ${prop.l3vpn.ac2_protocol}, + "static": { + "cascaded-lan-prefixes": [ + { + "ipv4-lan-prefixes": [ + { + "ip-prefix": ${prop.l3vpn.sna2-route.ip-prefix}, + "next-hop": ${prop.l3vpn.sna2-route.next-hop} + } + ] + } + ] + } + "bgp": { + "peers": [ + { + "peer-ip": ${prop.l3vpn.peer2-ip}, + "remote-as": ${prop.l3vpn.ac2_protocol_bgp_as} + } + ] + } + } + ] + }, + "vpn-attachment": { + "vpn-policy-id": ${prop.l3vpn.name} + } + } + ] + } + ] +} + diff --git a/plugins/restapi-call-node/provider/src/main/resources/l3smvpntemplate.json b/plugins/restapi-call-node/provider/src/main/resources/l3smvpntemplate.json new file mode 100644 index 000000000..30f30a24f --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/l3smvpntemplate.json @@ -0,0 +1,10 @@ +{ + "vpn-service": [ + { + "vpn-id": ${prop.l3vpn.name}, + "customer-name": "huawei", + "vpn-service-topology": ${prop.l3vpn.topology}, + "route-exchange-policy": "huawei-ac-net-l3vpn-svc:vpnv4-way" + } + ] +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/l3smvrftemplate.json b/plugins/restapi-call-node/provider/src/main/resources/l3smvrftemplate.json new file mode 100644 index 000000000..52359a7fd --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/l3smvrftemplate.json @@ -0,0 +1,14 @@ +{ + "vrf-attribute": [ + { + "vrf-attribute-id": ${prop.l3vpn.vrf1-id}, + "ne-id": ${prop.l3vpn.pe1_id}, + "vpn-policy-id": ${prop.l3vpn.vpn-policy1-id} + }, + { + "vrf-attribute-id": ${prop.l3vpn.vrf2-id}, + "ne-id": ${prop.l3vpn.pe2_id}, + "vpn-policy-id": ${prop.l3vpn.vpn-policy2-id} + } + ] +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.json b/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.json new file mode 100644 index 000000000..f185c8f5d --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.json @@ -0,0 +1,22 @@ +{ +"input": + { + "sdnc-request-header":{ + "svc-request-id": ${service-topology-operation-input.sdnc-request-header.svc-request-id}, + "svc-notification-url": ${service-topology-operation-input.sdnc-request-header.svc-notification-url} + }, + "request-information":{ + "request-id": ${service-topology-operation-input.request-information.request-id}, + "request-action": ${service-topology-operation-input.request-information.request-action}, + "request-sub-action": ${service-topology-operation-input.request-information.request-sub-action}, + "source": ${service-topology-operation-input.request-information.source}, + "notification-url": ${service-topology-operation-input.request-information.notification-url} + }, + "service-information":{ + "service-type": ${service-topology-operation-input.service-information.service-type}, + "service-instance-id": ${service-topology-operation-input.service-information.service-instance-id}, + "subscriber-name": ${service-topology-operation-input.service-information.subscriber-name}, + "subscriber-global-id": ${service-topology-operation-input.service-information.subscriber-global-id} + } +} + diff --git a/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.xml b/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.xml new file mode 100644 index 000000000..aea0625c1 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/northbound-api-template.xml @@ -0,0 +1,43 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + + <input xmlns="${northbound-xmlNS}"> + <${northbound-Container}> + <request-information> + <notification-url>${service-topology-operation-input.request-information.notification-url}</notification-url> + <request-action>${service-topology-operation-input.request-information.request-action}</request-action> + <request-id>${service-topology-operation-input.request-information.request-id}</request-id> + <request-sub-action>${service-topology-operation-input.request-information.request-sub-action}</request-sub-action> + <source>${service-topology-operation-input.request-information.source}</source> + </request-information> + <sdnc-request-header> + <svc-notification-url>${service-topology-operation-input.sdnc-request-header.svc-notification-url}</svc-notification-url> + <svc-request-id>${service-topology-operation-input.sdnc-request-header.svc-request-id}</svc-request-id> + </sdnc-request-header> + <service-information> + <service-instance-id>${service-topology-operation-input.service-information.service-instance-id}</service-instance-id> + <service-type>${service-topology-operation-input.service-information.service-type}</service-type> + <subscriber-global-id>${service-topology-operation-input.service-information.subscriber-global-id}</subscriber-global-id> + <subscriber-name>${service-topology-operation-input.service-information.subscriber-name}</subscriber-name> + </service-information> + ${'northbound-anyData} + </${northbound-Container}> + </input> diff --git a/plugins/restapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/restapi-call-node-blueprint.xml b/plugins/restapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/restapi-call-node-blueprint.xml new file mode 100755 index 000000000..390bb0a35 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/restapi-call-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="restapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode" /> + + + + <service ref="restapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/main/resources/service-configuration-notification-northbound-template.json b/plugins/restapi-call-node/provider/src/main/resources/service-configuration-notification-northbound-template.json new file mode 100644 index 000000000..c64661720 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/service-configuration-notification-northbound-template.json @@ -0,0 +1,23 @@ +{ + "input": { + "svc-request-id": ${service-configuration-notification-input.svc-request-id}, + "response-code": ${service-configuration-notification-input.response-code}, + "response-message": ${service-configuration-notification-input.response-message}, + "ack-final-indicator": ${service-configuration-notification-input.ack-final-indicator}, + "service-information": { + "service-type": ${service-data.service-information.service-type}, + "service-instance-id": ${service-data.service-information.service-instance-id}, + "subscriber-name": ${service-data.service-information.subscriber-name}, + "subscriber-global-id": ${service-data.service-information.subscriber-global-id} + }, + "response-parameters": [ + ${repeat:service-configuration-notification-input.response-parameters_length: + { + "sequence-number": ${service-configuration-notification-input.response-parameters[${1}].sequence-number}, + "tag-name": ${service-configuration-notification-input.response-parameters[${1}].tag-name}, + "tag-value": ${service-configuration-notification-input.response-parameters[${1}].tag-value} + } + } + ] + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/sptn-l3vpn-template.json b/plugins/restapi-call-node/provider/src/main/resources/sptn-l3vpn-template.json new file mode 100644 index 000000000..e850cb9a0 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/sptn-l3vpn-template.json @@ -0,0 +1,159 @@ +{ + "snc-l3vpn": { + "-xmlns": "urn:chinamobile:l3vpn", + "operate-status": "operate-up", + "traffic-class": "BE", + "acs": { + "l3-acs": [ + { + "id": "0", + "ne-id": ${prop.l3vpn.pe1_id}, + "ltp-id": ${prop.l3vpn.ac1_id}, + "ip-addr": ${prop.l3vpn.ac1_ip.value}, + "ip-mask": ${prop.l3vpn.ac1_ip.mask}, + "access-type": "Sub-interface", + "vlan": ${prop.l3vpn.ac1_svlan}, + "qos": { + "belonged-id": ${prop.l3vpn.ac1_id}, + "tunnel-mode": "pipeline", + "cac-mode": "1", + "convg-mode": "0", + "traffic-adj-mode": "0", + "a2z-policing": "1", + "z2a-policing": "1", + "a2z-cir": "5000", + "z2a-cir": "5000", + "a2z-pir": "1000000", + "z2a-pir": "1000000", + "a2z-cbs": "1024", + "z2a-cbs": "1024", + "a2z-pbs": "1024", + "z2a-pbs": "1024", + "a2z-color-mode": "0", + "z2a-color-mode": "0", + "traffic-class": "CS7" + }, + "protocol-list": { + "protocols": { + "protocol-type": "static", + "static-route-list": { + "static-routes": [ + { + "id": "0", + "dest-ip": ${prop.l3vpn.ac1_peer_ip.value}, + "dest-mask": ${prop.l3vpn.ac1_peer_ip.mask}, + "route-type": "direct-route", + "out-inf": ${prop.l3vpn.ac1_id}, + "route-weight": "3", + "route-status": "up" + } + ] + } + } + } + }, + { + "id": "1", + "ne-id": ${prop.l3vpn.pe2_id}, + "ltp-id": ${prop.l3vpn.ac2_id}, + "ip-addr": ${prop.l3vpn.ac2_ip.value}, + "ip-mask": ${prop.l3vpn.ac2_ip.mask}, + "access-type": "Sub-interface", + "vlan": ${prop.l3vpn.ac1_svlan}, + "qos": { + "belonged-id": ${prop.l3vpn.ac2_id}, + "tunnel-mode": "pipeline", + "cac-mode": "1", + "convg-mode": "0", + "traffic-adj-mode": "0", + "a2z-policing": "1", + "z2a-policing": "1", + "a2z-cir": "5000", + "z2a-cir": "5000", + "a2z-pir": "1000000", + "z2a-pir": "1000000", + "a2z-cbs": "1024", + "z2a-cbs": "1024", + "a2z-pbs": "1024", + "z2a-pbs": "1024", + "a2z-color-mode": "0", + "z2a-color-mode": "0", + "traffic-class": "CS7" + }, + "protocol-list": { + "protocols": { + "protocol-type": "static", + "static-route-list": { + "static-routes": [ + { + "id": "0", + "dest-ip": ${prop.l3vpn.ac2_peer_ip.value}, + "dest-mask": ${prop.l3vpn.ac2_peer_ip.mask}, + "route-type": "direct-route", + "out-inf": ${prop.l3vpn.ac2_id}, + "route-weight": "4", + "route-status": "up" + } + ] + } + } + } + } + ] + }, + "user-label": ${prop.l3vpn.name}, + "topo-mode": "any-to-any", + "admin-status": "admin-up", + "tunnel-create-policy": { + "user-label": "tunnel", + "tenant-id": "tunnel", + "direction": "bidirection", + "type": "1", + "admin-status": "admin-up", + "qos": { + "tunnel-mode": "pipeline", + "cac-mode": "1", + "convg-mode": "0", + "traffic-adj-mode": "0", + "a2z-policing": "0", + "a2z-cir": "500", + "a2z-pir": "1000000", + "a2z-cbs": "1024", + "a2z-pbs": "1024", + "a2z-color-mode": "0", + "z2a-policing": "0", + "z2a-cir": "500", + "z2a-pir": "1000000", + "z2a-cbs": "1024", + "z2a-pbs": "1024", + "z2a-color-mode": "0", + "traffic-class": "CS7" + }, + "snc-switch": { + "layer-rate": "LSP", + "linear-protection-type": "path-protection-1-to-1", + "linear-protection-protocol": "APS", + "switch-mode": "double-end-switch", + "revertive-mode": "revertive", + "wtr": "60", + "hold-off-time": "0", + "reroute-revertive-mode": "revertive", + "reroute-wtr": "50" + }, + "lsp-oam": { + "cc-allow": "true", + "cc-exp": "CS7", + "cc-interval": "3.3", + "lm-mode": "preactive", + "dm-mode": "preactive", + "meps": { + "mep": [ + { "id": "1" }, + { "id": "2" } + ] + } + } + } + } +} + diff --git a/plugins/restapi-call-node/provider/src/main/resources/update-vpe-data-with-apply-group.json b/plugins/restapi-call-node/provider/src/main/resources/update-vpe-data-with-apply-group.json new file mode 100644 index 000000000..979857521 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/update-vpe-data-with-apply-group.json @@ -0,0 +1,32 @@ +{ + "vrf-export-details": [ + ${repeat:restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.VrfExport_length: + { + "vrf-export": ${restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.VrfExport[${1}]} + } + } + ], + "vrf-import-details": [ + ${repeat:restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.VrfImport_length: + { + "vrf-import": ${restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.VrfImport[${1}]} + } + } + ], + "apply-group-template": [ + ${repeat:restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.ApplyGroup_length: + { + "apply-group": ${restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.ApplyGroup[${1}].ApplyGroup} + } + } + ], + "community-list": [ + ${repeat:restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.CommunityList_length: + { + "member": ${restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.CommunityList[${1}].member}, + "name": ${restapi-result.ApplyGroupResponse.ApplyGroupResponseData[0].VrfDetails.CommunityList[${1}].name} + } + } + ] +} + diff --git a/plugins/restapi-call-node/provider/src/main/resources/vnf-information-update.json b/plugins/restapi-call-node/provider/src/main/resources/vnf-information-update.json new file mode 100644 index 000000000..d554624c7 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vnf-information-update.json @@ -0,0 +1,3 @@ +{ +"generic-vnf-service" :${vnf-service.generic-vnf-service} +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/vpn-allocation-request.json b/plugins/restapi-call-node/provider/src/main/resources/vpn-allocation-request.json new file mode 100644 index 000000000..7a689bd5c --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vpn-allocation-request.json @@ -0,0 +1,15 @@ +{ + "AllocateVpnResourcesRequest": { + "message-id": "${service-data.oper-status.modify-timestamp}", + "service-instance-id": "${service-data.service-information.service-instance-id}", + "vrf-request-type": { + "vpn-id": "${service-data.avpn-logicalchannel-information.vpn-id}", + "vpn-service": "${service-data.service-information.service-type}", + "e2e-vpn-id": "${service-data.avpn-logicalchannel-information.e2e-vpn-id}", + "vpe-name": "${service-data.avpn-logicalchannel-information.evc-endpoint-information.vpe-name}", + "asn": "${service-data.avpn-logicalchannel-information.carrier-asn}", + "route-group-name": "${service-data.avpn-logicalchannel-information.bgp-options.neighbor-address-information.route-group-name}", + "hub-or-spoke": "${service-data.avpn-logicalchannel-information.hub-or-spoke}" + } + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/vpn-information-update.json b/plugins/restapi-call-node/provider/src/main/resources/vpn-information-update.json new file mode 100644 index 000000000..3ac16e291 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vpn-information-update.json @@ -0,0 +1,3 @@ +{ +"vpn-information" :${vpe-vpn-service.vpn-information} +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/vrf-service-configuration-information-template.json b/plugins/restapi-call-node/provider/src/main/resources/vrf-service-configuration-information-template.json new file mode 100644 index 000000000..c6defb405 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vrf-service-configuration-information-template.json @@ -0,0 +1,16 @@ +{ + "input": { + + "e2e-vpn-key": ${service-data.avpn-logicalchannel-information.e2e-vpn-id}, + "logical-channel-id": ${service-data.service-information.service-instance-id}, + "vpe-name": ${service-data.avpn-logicalchannel-information.evc-endpoint-information.vpe-name}, + "rpc-action": ${tmp.rpc-action}, + + "vpn-information": ${vpe-vpn-service.vpn-information}, + "vrf-details": ${vpe-vpn-service.vpn-information.vrf-details}, + "vrf-vlan-resources": { + "logical-channel-id": ${service-data.service-information.service-instance-id}, + "logical-channel-status": ${tmp.logical-channel-status} + } + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/vrf-update-vlan-status-template.json b/plugins/restapi-call-node/provider/src/main/resources/vrf-update-vlan-status-template.json new file mode 100644 index 000000000..f6381256f --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vrf-update-vlan-status-template.json @@ -0,0 +1,12 @@ +{ + "input": { + "e2e-vpn-key": ${service-data.avpn-logicalchannel-information.e2e-vpn-id}, + "logical-channel-id": ${service-data.service-information.service-instance-id}, + "vpe-name": ${service-data.avpn-logicalchannel-information.evc-endpoint-information.vpe-name}, + "rpc-action": ${tmp.rpc-action}, + "vrf-vlan-resources": { + "logical-channel-id": ${service-data.service-information.service-instance-id}, + "logical-channel-status": ${tmp.logical-channel-status} + } + } +} diff --git a/plugins/restapi-call-node/provider/src/main/resources/vrf-update.json b/plugins/restapi-call-node/provider/src/main/resources/vrf-update.json new file mode 100644 index 000000000..cdaef4d82 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/main/resources/vrf-update.json @@ -0,0 +1,56 @@ +{ + "vpn-vame": ${allocate-vpn-resource-notification-input.vpn-data[0].vpn-name}, + "vpn-id": ${allocate-vpn-resource-notification-input.vpn-data[0].vpn-id}, + "vrf-details": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].vrf-information_length: + { + "vrf-export-details": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vrf-export-details_length: + { + "vrf-export": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vrf-export-details[${2}].vrf-export} + } + } + ], + "vrf-name": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vrf-name}, + "vpe-name": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vpe-name}, + "vrf-import-details": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vrf-import-details_length: + { + "vrf-import": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].vrf-import-details[${2}].vrf-import} + } + } + ], + "apply-group-template": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].apply-group-template_length: + { + "apply-group": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].apply-group-template[${2}].apply-group} + } + } + ], + "community-list": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].community-list_length: + { + "member": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].community-list[${2}].name}, + "name": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].community-list[${2}].member} + } + } + ], + "router-distinguisher": ${allocate-vpn-resource-notification-input.vpn-data[0].vrf-information[${1}].router-distinguisher} + } + } + ], + "spoke-routes": { + "route-target": ${allocate-vpn-resource-notification-input.vpn-data[0].spoke-route-target.route-target} + }, + + "route-target-details": [ + ${repeat:allocate-vpn-resource-notification-input.vpn-data[0].route-target-details_length: + { + "route-target": ${allocate-vpn-resource-notification-input.vpn-data[0].route-target-details[${1}].route-target}, + "route-target-type": ${allocate-vpn-resource-notification-input.vpn-data[0].route-target-details[${1}].route-target-type} + } + } + ], + + "e2e-vpn-key": ${allocate-vpn-resource-notification-input.vpn-data[0].e2e-vpn-id} +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MockCookieAuthServer.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MockCookieAuthServer.java new file mode 100644 index 000000000..7e1e0d505 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MockCookieAuthServer.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.NewCookie; +import javax.ws.rs.core.Response; + +@Path("get-cookie") +public class MockCookieAuthServer { + @GET + @Path("cookie") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response getCookie() { + return Response.status(200).entity("success").cookie(new NewCookie("cookieResponse", "cookieValueInReturn")).build(); + } +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MultipartServerMock.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MultipartServerMock.java new file mode 100644 index 000000000..00b5c324d --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/MultipartServerMock.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataParam; + +import javax.ws.rs.Consumes; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.InputStream; + +@Path("file-upload") +public class MultipartServerMock { + + @POST + @Path("upload") + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Response uploadFile( + @FormDataParam("file") InputStream inputStream, + @FormDataParam("file") FormDataContentDisposition fileDetail) { + return Response.status(200).entity(fileDetail.getFileName()).build(); + } +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java new file mode 100644 index 000000000..cdffd4576 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static org.junit.Assert.assertEquals; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +public class TestJsonParser { + + @Test + public void test() throws SvcLogicException, IOException { + String path = "src/test/resources/test.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + assertEquals("Server1", mm.get("equipment-data[0].equipment-id")); + assertEquals("1600000", mm.get("equipment-data[0].max-server-speed")); + assertEquals("2", mm.get("equipment-data[0].number-primary-servers")); + assertEquals("4", mm.get("equipment-data[0].server-count")); + assertEquals("Server1", mm.get("equipment-data[0].server-id")); + assertEquals("Unknown", mm.get("equipment-data[0].server-model")); + assertEquals("Test-Value", mm.get("equipment-data[0].test-node.test-inner-node")); + assertEquals("1", mm.get("equipment-data_length")); + assertEquals("The provisioned access bandwidth is at or exceeds 50% of the total server capacity.", + mm.get("message")); + assertEquals("VCE-Cust", mm.get("resource-rule.endpoint-position")); + assertEquals("Server", mm.get("resource-rule.equipment-level")); + assertEquals("max-server-speed * number-primary-servers", mm.get("resource-rule.hard-limit-expression")); + assertEquals("Bandwidth", mm.get("resource-rule.resource-name")); + assertEquals("DUMMY", mm.get("resource-rule.service-model")); + assertEquals("0.6 * max-server-speed * number-primary-servers", mm.get("resource-rule.soft-limit-expression")); + assertEquals("1605000", mm.get("resource-state.last-added")); + assertEquals("1920000", mm.get("resource-state.limit-value")); + assertEquals("1600000", mm.get("resource-state.threshold-value")); + assertEquals("1605000", mm.get("resource-state.used")); + } + + @Test(expected = NullPointerException.class) + public void testNullString() throws SvcLogicException { + JsonParser.convertToProperties(null); + } + + @Test + public void testJsonStringToCtxToplevelArray() throws Exception { + String path = "src/test/resources/ArrayMenu.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + + assertEquals("1000", mm.get("[0].calories")); + assertEquals("1", mm.get("[0].id")); + assertEquals("plain", mm.get("[0].name")); + assertEquals("pizza", mm.get("[0].type")); + assertEquals("true", mm.get("[0].vegetarian")); + assertEquals("2000", mm.get("[1].calories")); + assertEquals("2", mm.get("[1].id")); + assertEquals("Tuesday Special", mm.get("[1].name")); + assertEquals("1", mm.get("[1].topping[0].id")); + assertEquals("onion", mm.get("[1].topping[0].name")); + assertEquals("2", mm.get("[1].topping[1].id")); + assertEquals("pepperoni", mm.get("[1].topping[1].name")); + assertEquals("2", mm.get("[1].topping_length")); + assertEquals("pizza", mm.get("[1].type")); + assertEquals("false", mm.get("[1].vegetarian")); + assertEquals("1500", mm.get("[2].calories")); + assertEquals("3", mm.get("[2].id")); + assertEquals("House Special", mm.get("[2].name")); + assertEquals("3", mm.get("[2].topping[0].id")); + assertEquals("basil", mm.get("[2].topping[0].name")); + assertEquals("4", mm.get("[2].topping[1].id")); + assertEquals("fresh mozzarella", mm.get("[2].topping[1].name")); + assertEquals("5", mm.get("[2].topping[2].id")); + assertEquals("tomato", mm.get("[2].topping[2].name")); + assertEquals("3", mm.get("[2].topping_length")); + assertEquals("pizza", mm.get("[2].type")); + assertEquals("true", mm.get("[2].vegetarian")); + assertEquals("3", mm.get("_length")); + } + + @Test + public void testJsonStringToCtx() throws Exception { + String path = "src/test/resources/ObjectMenu.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + assertEquals("1000", mm.get("menu[0].calories")); + assertEquals("1", mm.get("menu[0].id")); + assertEquals("plain", mm.get("menu[0].name")); + assertEquals("pizza", mm.get("menu[0].type")); + assertEquals("true", mm.get("menu[0].vegetarian")); + assertEquals("2000", mm.get("menu[1].calories")); + assertEquals("2", mm.get("menu[1].id")); + assertEquals("Tuesday Special", mm.get("menu[1].name")); + assertEquals("1", mm.get("menu[1].topping[0].id")); + assertEquals("onion", mm.get("menu[1].topping[0].name")); + assertEquals("2", mm.get("menu[1].topping[1].id")); + assertEquals("pepperoni", mm.get("menu[1].topping[1].name")); + assertEquals("2", mm.get("menu[1].topping_length")); + assertEquals("pizza", mm.get("menu[1].type")); + assertEquals("false", mm.get("menu[1].vegetarian")); + assertEquals("1500", mm.get("menu[2].calories")); + assertEquals("3", mm.get("menu[2].id")); + assertEquals("House Special", mm.get("menu[2].name")); + assertEquals("3", mm.get("menu[2].topping[0].id")); + assertEquals("basil", mm.get("menu[2].topping[0].name")); + assertEquals("4", mm.get("menu[2].topping[1].id")); + assertEquals("fresh mozzarella", mm.get("menu[2].topping[1].name")); + assertEquals("5", mm.get("menu[2].topping[2].id")); + assertEquals("tomato", mm.get("menu[2].topping[2].name")); + assertEquals("3", mm.get("menu[2].topping_length")); + assertEquals("pizza", mm.get("menu[2].type")); + assertEquals("true", mm.get("menu[2].vegetarian")); + assertEquals("3", mm.get("menu_length")); + } + + @Test + public void test1dJsonStringToCtx() throws Exception { + String path = "src/test/resources/1dArray.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + + System.out.println(mm); + assertEquals("6", mm.get("_length")); + assertEquals("apple", mm.get("[0]")); + assertEquals("orange", mm.get("[1]")); + assertEquals("banana", mm.get("[2]")); + assertEquals("squash", mm.get("[3]")); + assertEquals("broccoli", mm.get("[4]")); + assertEquals("cauliflower", mm.get("[5]")); + } + + @Test + public void test2dJsonStringToCtx() throws Exception { + String path = "src/test/resources/2dArray.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + + // code will crash before these tests + assertEquals("apple", mm.get("[0][0]")); + assertEquals("orange", mm.get("[0][1]")); + assertEquals("banana", mm.get("[0][2]")); + assertEquals("3", mm.get("[0]_length")); + + assertEquals("squash", mm.get("[1][0]")); + assertEquals("broccoli", mm.get("[1][1]")); + assertEquals("cauliflower", mm.get("[1][2]")); + assertEquals("3", mm.get("[1]_length")); + assertEquals("2", mm.get("_length")); + } + + @Test + public void test3dJsonStringToCtx() throws Exception { + String path = "src/test/resources/3dArray.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + + assertEquals("3", mm.get("_length")); + assertEquals("1", mm.get("[0]_length")); + assertEquals("3", mm.get("[0][0]_length")); + + assertEquals("2", mm.get("[1]_length")); + assertEquals("3", mm.get("[1][0]_length")); + assertEquals("3", mm.get("[1][1]_length")); + + assertEquals("3", mm.get("[2]_length")); + assertEquals("3", mm.get("[2][0]_length")); + assertEquals("3", mm.get("[2][1]_length")); + assertEquals("3", mm.get("[2][2]_length")); + + assertEquals("x", mm.get("[0][0][0]")); + assertEquals("y", mm.get("[0][0][1]")); + assertEquals("z", mm.get("[0][0][2]")); + + assertEquals("abc", mm.get("[1][0][0]")); + assertEquals("def", mm.get("[1][0][1]")); + assertEquals("xyz", mm.get("[1][0][2]")); + + assertEquals("123", mm.get("[1][1][0]")); + assertEquals("456", mm.get("[1][1][1]")); + assertEquals("789", mm.get("[1][1][2]")); + + assertEquals("a", mm.get("[2][0][0]")); + assertEquals("b", mm.get("[2][0][1]")); + assertEquals("c", mm.get("[2][0][2]")); + + assertEquals("d", mm.get("[2][1][0]")); + assertEquals("e", mm.get("[2][1][1]")); + assertEquals("f", mm.get("[2][1][2]")); + + assertEquals("1", mm.get("[2][2][0]")); + assertEquals("2", mm.get("[2][2][1]")); + assertEquals("3", mm.get("[2][2][2]")); + } + + @Test + public void testJsonWidgetStringToCtx() throws Exception { + String path = "src/test/resources/Widget.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + assertEquals("false", mm.get("widget.debug")); + assertEquals("center", mm.get("widget.image.alignment")); + assertEquals("150", mm.get("widget.image.hOffset")); + assertEquals("moon", mm.get("widget.image.name")); + assertEquals("images/moon.png", mm.get("widget.image.src")); + assertEquals("150", mm.get("widget.image.vOffset")); + assertEquals("center", mm.get("widget.text.alignment")); + assertEquals("Click Me", mm.get("widget.text.data")); + assertEquals("350", mm.get("widget.text.hOffset")); + assertEquals("text1", mm.get("widget.text.name")); + assertEquals("21", mm.get("widget.text.size")); + assertEquals("bold", mm.get("widget.text.style")); + assertEquals("200", mm.get("widget.text.vOffset")); + assertEquals("300", mm.get("widget.window.height")); + assertEquals("main_window", mm.get("widget.window.name")); + assertEquals("ONAP Widget", mm.get("widget.window.title")); + assertEquals("200", mm.get("widget.window.width")); + } + + @Test + public void testEmbeddedEscapedJsonJsonStringToCtx() throws Exception { + String path = "src/test/resources/EmbeddedEscapedJson.json"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Map<String, String> mm = JsonParser.convertToProperties(content); + assertEquals("escapedJsonObject", mm.get("input.parameters[0].name")); + assertEquals("[{\"id\":\"0.2.0.0/16\"},{\"id\":\"ge04::/64\"}]", mm.get("input.parameters[0].value")); + assertEquals("Hello/World", mm.get("input.parameters[1].value")); + assertEquals("resourceName", mm.get("input.parameters[2].name")); + assertEquals("The\t\"Best\"\tName", mm.get("input.parameters[2].value")); + assertEquals("3", mm.get("input.parameters_length")); + + // Break the embedded json object into properties + mm = JsonParser.convertToProperties(mm.get("input.parameters[0].value")); + assertEquals("0.2.0.0/16", mm.get("[0].id")); + assertEquals("ge04::/64", mm.get("[1].id")); + assertEquals("2", mm.get("_length")); + } + +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java new file mode 100755 index 000000000..a993bb946 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java @@ -0,0 +1,604 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import org.codehaus.jettison.json.JSONObject; +import org.junit.Before; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestRestapiCallNode { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(TestRestapiCallNode.class); + + @Before + public void init() { + System.setProperty("SDNC_CONFIG_DIR", "src/test/resources"); + } + + @Test + public void testDelete() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<>(); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testDeleteWithPayload() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + ctx.setAttribute("prop.name", "site1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/sdwan-site.json"); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testSendFile() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<>(); + p.put("fileName", "src/test/resources/test_file.txt"); + p.put("url", "https://testurl.test"); + p.put("user", "user"); + p.put("password", "*******"); + p.put("skipSending", "true"); // Set real url, user, password, when testing actual sending + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendFile(p, ctx); + } + + @Test + public void testJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "3"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].clci", "clci"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testInvalidRepeatTimes() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "a"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testInvalidTemplatePath() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resourcess/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testWithoutSkipSending() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "false"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + + @Test(expected = SvcLogicException.class) + public void testWithInvalidURI() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo. getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "false"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testVpnJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testSiteJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + ctx.setAttribute("prop.l3vpn.site1_name", "10000000-0000-0000-0000-000000000002"); + ctx.setAttribute("prop.l3vpn.vpn-policy1-id", "10000000-0000-0000-0000-000000000003"); + ctx.setAttribute("prop.l3vpn.entry1-id", "1"); + ctx.setAttribute("prop.l3vpn.sna1_name", "10000000-0000-0000-0000-000000000004"); + ctx.setAttribute("prop.l3vpn.pe1_id", "a8098c1a-f86e-11da-bd1a-00112444be1e"); + ctx.setAttribute("prop.l3vpn.ac1_id", "a8098c1a-f86e-11da-bd1a-00112444be1b"); + ctx.setAttribute("prop.l3vpn.ac1-peer-ip", "192.168.1.1"); + ctx.setAttribute("prop.l3vpn.ac1-ip", "192.168.1.2"); + ctx.setAttribute("prop.l3vpn.sna1_svlan", "100"); + ctx.setAttribute("prop.l3vpn.ac1_protocol", "static"); + ctx.setAttribute("prop.l3vpn.sna1-route.ip-prefix", "192.168.1.1/24"); + ctx.setAttribute("prop.l3vpn.sna1-route.next-hop", "192.168.1.4"); + + ctx.setAttribute("prop.l3vpn.site2_name", "10000000-0000-0000-0000-000000000005"); + ctx.setAttribute("prop.l3vpn.vpn-policy2-id", "10000000-0000-0000-0000-000000000006"); + ctx.setAttribute("prop.l3vpn.entry2-id", "1"); + ctx.setAttribute("prop.l3vpn.sna2_name", "10000000-0000-0000-0000-000000000007"); + ctx.setAttribute("prop.l3vpn.pe2_id", "a8098c1a-f86e-11da-bd1a-00112444be1a"); + ctx.setAttribute("prop.l3vpn.ac2_id", "a8098c1a-f86e-11da-bd1a-00112444be1c"); + ctx.setAttribute("prop.l3vpn.ac2-peer-ip", "192.168.1.6"); + ctx.setAttribute("prop.l3vpn.ac2-ip", "192.168.1.5"); + ctx.setAttribute("prop.l3vpn.sna2_svlan", "200"); + ctx.setAttribute("prop.l3vpn.ac2_protocol", "bgp"); + ctx.setAttribute("prop.l3vpn.peer2-ip", "192.168.1.7"); + ctx.setAttribute("prop.l3vpn.ac2_protocol_bgp_as", "200"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smsitetemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/sites"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testVrfJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.vrf1-id", "10000000-0000-0000-0000-000000000007"); + ctx.setAttribute("prop.l3vpn.vpn-policy1-id", "10000000-0000-0000-0000-000000000003"); + ctx.setAttribute("prop.l3vpn.pe1_id", "a8098c1a-f86e-11da-bd1a-00112444be1e"); + ctx.setAttribute("prop.l3vpn.vrf2-id", "10000000-0000-0000-0000-000000000009"); + ctx.setAttribute("prop.l3vpn.vpn-policy2-id", "10000000-0000-0000-0000-000000000006"); + ctx.setAttribute("prop.l3vpn.pe2_id", "a8098c1a-f86e-11da-bd1a-00112444be1a"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvrftemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vrf-attributes"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testDeleteVpnJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + //p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services" + + "/vpnservice=10000000-0000-0000-0000-000000000001"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "delete"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testL2DciTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.dci-connects.id", "Id1"); + ctx.setAttribute("prop.dci-connects.name", "Name1"); + ctx.setAttribute("prop.dci-connects.local_networks[0]", "NetId1"); + ctx.setAttribute("prop.dci-connects.local_networks[1]", "NetId2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[0]", "100:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[0]", "100:2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[1]", "200:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[1]", "200:2"); + ctx.setAttribute("prop.dci-connects.vni", "1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l2-dci-connects-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testL3DciTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.dci-connects.id", "Id1"); + ctx.setAttribute("prop.dci-connects.name", "Name1"); + ctx.setAttribute("prop.dci-connects.local_networks_length", "2"); + ctx.setAttribute("prop.dci-connects.local_networks[0]", "NetId1"); + ctx.setAttribute("prop.dci-connects.local_networks[1]", "NetId2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[0]", "100:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[0]", "100:2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[1]", "200:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[1]", "200:2"); + ctx.setAttribute("prop.dci-connects.vni", "1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3-dci-connects-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + + } + + @Test + public void testControllerTokenTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.sdncRestApi.thirdpartySdnc.user", "admin"); + ctx.setAttribute("prop.sdncRestApi.thirdpartySdnc.password", "admin123"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/actokentemplate.json"); + p.put("restapiUrl", "https://ipwan:18002/controller/v2/tokens"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + + @Test + public void testDeleteNoneAsContentType() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<>(); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("format", "none"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testPostNoneAsContentType() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "none"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + /* + * { + "partnerOne": { + "url": "http://localhost:7001" 4 http://uebsb93kcdc.it.att.com:3904", + "test": "/metrics" + }, + "partnerTwo": { + "url": "http://localhost:7002", + "user": "controller_user", + "password": "P@ssword", + "test": "/metrics" + }, + "partnerThree": { + "url": "http://localhost:7003", + "user": "controller_admin" + } +} + */ + @Test + public void testPartners() throws Exception{ + String partnerTwoKey = "partnerTwo"; + String partnerTwoUsername = "controller_user"; + String partnerTwoPassword = "P@ssword"; + + System.setProperty("SDNC_CONFIG_DIR", "src/test/resources"); + RestapiCallNode rcn = new RestapiCallNode(); + assertNull(rcn.partnerStore.get("partnerOne")); + PartnerDetails details = rcn.partnerStore.get(partnerTwoKey); + assertEquals(partnerTwoUsername,details.username); + assertEquals(partnerTwoPassword,details.password); + assertNull(rcn.partnerStore.get("partnerThree")); + + //In this scenario the caller expects username, password and url to be picked up from the partners json + Map<String, String> paramMap = new HashMap<>(); + paramMap.put("partner", partnerTwoKey); + rcn.handlePartner(paramMap ); + assertEquals(partnerTwoUsername,paramMap.get(RestapiCallNode.restapiUserKey)); + assertEquals(partnerTwoPassword,paramMap.get(RestapiCallNode.restapiPasswordKey)); + assertEquals("http://localhost:7002",paramMap.get(RestapiCallNode.restapiUrlString)); + + //In this scenario the caller expects username, password and url to be picked up from the partners json + //the provided suffix will be appended to the default url from the partners json + paramMap = new HashMap<>(); + paramMap.put("partner", partnerTwoKey); + paramMap.put("restapiUrlSuffix", "/networking/v1/instance/3"); + rcn.handlePartner(paramMap); + Parameters p = new Parameters(); + RestapiCallNode.getParameters(paramMap, p); + assertEquals(partnerTwoUsername,p.restapiUser); + assertEquals(partnerTwoPassword,p.restapiPassword); + assertEquals("http://localhost:7002/networking/v1/instance/3",p.restapiUrl); + } + + @Test + public void retryPolicyBean() throws Exception { + Integer retries = 3; + String first = "http://localhost:7001"; + String second = "http://localhost:7001"; + + RetryPolicy p = new RetryPolicy(new String[] {first,second}, retries); + assertEquals(retries,p.getMaximumRetries()); + assertNotNull(p.getRetryMessage()); + String next = p.getNextHostName(); + assertEquals(second,next); + assertEquals(1,p.getRetryCount()); + next = p.getNextHostName(); + assertEquals(first,next); + assertEquals(2,p.getRetryCount()); + } + + @Test + public void testEmbeddedJsonTemplate() throws Exception { + SvcLogicContext ctx = new SvcLogicContext(); + String complexObj = "{\"image_name\":\"Ubuntu 14.04\",\"service-instance-id\":\"1\",\"vnf-model-customization-uuid\":\"2f\",\"vnf-id\":\"3b\"}"; + ctx.setAttribute("reqId", "1235"); + ctx.setAttribute("subReqId", "054243"); + ctx.setAttribute("actionName", "CREATE"); + ctx.setAttribute("myPrefix", "2016-09-09 16:30:35.0"); + ctx.setAttribute("complexObj", complexObj); + RestapiCallNode rcn = new RestapiCallNode(); + String request = rcn.buildXmlJsonRequest(ctx, rcn.readFile("src/test/resources/testEmbeddedTemplate.json"), Format.JSON); + //This will throw a JSONException and fail the test case if rest api call node doesn't form valid JSON + assertNotNull(new JSONObject(request)); + } + + @Test + public void testMultiLineEmbeddedJsonTemplate() throws Exception { + SvcLogicContext ctx = new SvcLogicContext(); + String complexObj = "{\n" + + " \"image_name\": \"Ubuntu 14.04\",\n" + + " \"service-instance-id\": \"1\",\n" + + " \"vnf-model-customization-uuid\": \"2f\",\n" + + " \"vnf-id\": \"3b\"\n" + + "}"; + ctx.setAttribute("reqId", "1235"); + ctx.setAttribute("subReqId", "054243"); + ctx.setAttribute("actionName", "CREATE"); + ctx.setAttribute("myPrefix", "2016-09-09 16:30:35.0"); + ctx.setAttribute("complexObj", complexObj); + RestapiCallNode rcn = new RestapiCallNode(); + String request = rcn.buildXmlJsonRequest(ctx, rcn.readFile("src/test/resources/testMultiLineEmbeddedTemplate.json"), Format.JSON); + //This will throw a JSONException and fail the test case if rest api call node doesn't form valid JSON + assertNotNull(new JSONObject(request)); + } + + @Test + public void testGetMultipleUrls() throws Exception{ + String[] urls = RestapiCallNode.getMultipleUrls("http://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Banana,https://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Potato"); + assertEquals("http://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Banana",urls[0]); + assertEquals("https://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Potato",urls[1]); + + urls = RestapiCallNode.getMultipleUrls("https://wiki.onap.org/,http://localhost:7001/,http://wiki.onap.org/"); + assertEquals("https://wiki.onap.org/",urls[0]); + assertEquals("http://localhost:7001/",urls[1]); + assertEquals("http://wiki.onap.org/",urls[2]); + + urls = RestapiCallNode.getMultipleUrls("https://wiki.onap.org/test=4,5,6,http://localhost:7001/test=1,2,3,http://wiki.onap.org/test=7,8,9,10"); + assertEquals("https://wiki.onap.org/test=4,5,6",urls[0]); + assertEquals("http://localhost:7001/test=1,2,3",urls[1]); + assertEquals("http://wiki.onap.org/test=7,8,9,10",urls[2]); + + urls = RestapiCallNode.getMultipleUrls("https://wiki.onap.org/,https://readthedocs.org/projects/onap/"); + assertEquals("https://wiki.onap.org/",urls[0]); + assertEquals("https://readthedocs.org/projects/onap/",urls[1]); + } + + @Test + public void testContainsMultipleUrls() throws Exception{ + assertFalse(RestapiCallNode.containsMultipleUrls("https://wiki.onap.org/")); + assertFalse(RestapiCallNode.containsMultipleUrls("http://wiki.onap.org/")); + assertFalse(RestapiCallNode.containsMultipleUrls("http://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Banana")); + assertFalse(RestapiCallNode.containsMultipleUrls("https://localhost:8008/params=1,2,3,4,5,6")); + + assertTrue(RestapiCallNode.containsMultipleUrls("https://wiki.onap.org/,https://readthedocs.org/projects/onap/")); + assertTrue(RestapiCallNode.containsMultipleUrls("http://localhost:7001/,http://localhost:7002")); + assertTrue(RestapiCallNode.containsMultipleUrls("http://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Banana,https://localhost:8008/rest/restconf/data/abc:def/abc:action=Create,deviceType=Potato")); + assertTrue(RestapiCallNode.containsMultipleUrls("https://wiki.onap.org/,http://localhost:7001/,http://wiki.onap.org/")); + assertTrue(RestapiCallNode.containsMultipleUrls("https://wiki.onap.org/test=4,5,6,http://localhost:7001/test=1,2,3,http://wiki.onap.org/test=7,8,9,10")); + } + +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java new file mode 100644 index 000000000..ac6d3b78b --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java @@ -0,0 +1,321 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.onap.ccsdk.sli.plugins.restapicall.XmlJsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class TestXmlJsonUtil { + + private static final Logger log = LoggerFactory.getLogger(TestXmlJsonUtil.class); + + @Test + public void test() { + Map<String, String> mm = new HashMap<>(); + mm.put("service-data.vnf-config-parameters-list.vnf-config-parameters[0].vnf-type", "N-SBG"); + mm.put("service-data.service-information.service-instance-id", "someinstance001"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-server-ip-address", "10.11.12.13"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].escf-domain-name", "hclab.atttest.com"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3_length", "2"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-target-v3-id", "1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-target-ip-address", "127.0.0.1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-security-level", "NO_AUTH_NO_PRIV"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-target-v3-id", "2"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-target-ip-address", "192.168.1.8"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-security-level", "NO_AUTH_NO_PRIV"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-ip-address-1", "2001:1890:1001:2224::1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-ip-address-2", "2001:1890:1001:2424::1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].diameter-rf-realm-name", "uvp.els-an.att.net"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].diameter-rf-peer-ip-address", "192.168.1.66"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].bgf-controller-ip-address", "192.168.1.186"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].bgf-control-link-name", "mg3/69@192.168.1.226"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].rf-interface-nexthop-ip-address", "10.111.108.150"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].rf-mated-pair-ip-address", "10.111.108.146"); + + mm.put("service-data.vnf-config-parameters-list.vnf-config-parameters[0].access-net-pcscf_length", "4"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[0].network-name", "UvpbUgnAccess1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[0].sip-pa-termination-ip-address", "10.111.108.146"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].network-name", "MIS"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].next-hop-ip-address", "10.111.108.158"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].subnet-mask-length", "10.111.108.154"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].network-name", "AVPN1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].next-hop-ip-address", "10.111.108.166"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].subnet-mask-length", "10.111.108.162"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].network-name", "AVPN1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].next-hop-ip-address", "10.129.108.166"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].subnet-mask-length", "10.129.108.162"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf_length", "1"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].network-name", "Core"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].next-hop-ip-address", "10.111.108.142"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].sip-pa-termination-ip-address", "10.111.108.138"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].mated-pair-fully-qualified-domain-name", + "mt1nj01sbg01pyl-mt1nj01sbg02pyl.ar1ga.uvp.els-an.att.net"); + + mm.put("service-data.appc-request-header.svc-request-id", "SOMESERVICEREQUEST123451000"); + mm.put("service-data.vnf-config-information.vnf-host-ip-address", "192.168.13.151"); + mm.put("service-data.vnf-config-information.vendor", "Netconf"); + + mm.put("service-data.vnf-config-information.escape-test", + "blah blah \"xxx&nnn<>\\'\"there>blah<&''\"\"123\\\\\\'''blah blah &"); + + String ss = XmlJsonUtil.getXml(mm, "service-data.vnf-config-parameters-list"); + log.info(ss); + + ss = XmlJsonUtil.getXml(mm, "service-data.vnf-config-information"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-parameters-list.vnf-config-parameters"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-information"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-information.vnf-host-ip-address"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "\"service-data.vnf-config-information.vnf-host-ip-address"); + log.info(ss); + } + + public void testRemoveEmptyStructJson1() { + String xmlin = "{\n" + + " \"policyName\" : \"default-domain.ECOM_Tenant_DND.ECOM_Tenant_DND_mtn6_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_landing-network-role_policy_05\",\n" + + " \"policyConfigType\": \"MicroService\",\n" + + " \"ecompName\": \"SDNC\",\n" + + " \"configBody\": \"{\\\"service\\\": \\\"NetworkPolicy\\\"," + + "\\\"location\\\": \\\"Search\\\",\\\"uuid\\\": \\\"Search\\\"," + + "\\\"policyName\\\": \\\"default-domain.ECOM_Tenant_DND.ECOM_Tenant_DND_mtn6_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_landing-network-role_policy_05\\\"," + + "\\\"description\\\": \\\"default-domain.ECOM_Tenant_DND.ECOM_Tenant_DND_mtn6_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_landing-network-role_policy_05\\\",\\\"configName\\\": \\\"Search\\\"," + + "\\\"templateVersion\\\": \\\"1607\\\",\\\"version\\\": \\\"1.3.0.1\\\"," + + "\\\"priority\\\": \\\"2\\\",\\\"policyScope\\\": \\\"SDNC\\\",\\\"riskType\\\": \\\"low\\\"," + + "\\\"riskLevel\\\": \\\"2\\\",\\\"guard\\\": \\\"True\\\",\\\"content\\\":{ " + + "\\\"network-policy\\\": { \\\"display_name\\\": \\\"default-domain.ECOM_Tenant_DND." + + "ECOM_Tenant_DND_mtn6_HngwOamNetVto.HNGWOAMNETVTO.OAM_landing-network-role_policy_05\\\", " + + "\\\"fq_name\\\": [ \\\"default-domain\\\", " + + "\\\"ECOM_Tenant_DND\\\", \\\"default-domain." + + "ECOM_Tenant_DND.ECOM_Tenant_DND_mtn6_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_landing-network-role_policy_05\\\" ], " + + "\\\"id_perms\\\": { \\\"user_visible\\\": true }, " + + "\\\"parent_type\\\": \\\"project\\\", \\\"network_policy_entries\\\": { " + + "\\\"policy_rule\\\": [ { " + + "\\\"action_list\\\": { \\\"apply_service\\\": [ " + + "\\\"testfqdn\\\" ], " + + "\\\"gateway_name\\\": null, \\\"qos_action\\\": null, " + + "\\\"log\\\": false , \\\"mirror_to\\\": null, " + + "\\\"simple_action\\\": null }, " + + "\\\"ethertype\\\": null, \\\"application\\\": [], " + + "\\\"direction\\\": \\\"<>\\\", \\\"dst_addresses\\\": [ " + + "{ \\\"network_policy\\\": null, " + + "\\\"security_group\\\": null, " + + "\\\"subnet\\\": null, \\\"virtual_network\\\": " + + "\\\"default-domain:ECOM_Tenant_DND:ECOM_Tenant_DND_int_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_net_10\\\" } ], " + + "\\\"dst_ports\\\": [ { " + + "\\\"end_port\\\": -1, \\\"start_port\\\": -1 " + + "} ], \\\"protocol\\\": \\\"any\\\", " + + "\\\"src_addresses\\\": [ { " + + "\\\"network_policy\\\": null, " + + "\\\"security_group\\\": null, " + + "\\\"subnet\\\": null, \\\"virtual_network\\\": " + + "\\\"default-domain:ECOM_Tenant_DND:ECOM_Tenant_DND_int_HngwOamNetVto." + + "HNGWOAMNETVTO.OAM_net_9\\\" } ], " + + "\\\"src_ports\\\": [ { " + + "\\\"end_port\\\": -1, " + + "\\\"start_port\\\": -1 } ] } " + + "] } }}}\"\n" + + "}"; + + String xmloutexpected = xmlin; + + String xmlout = XmlJsonUtil.removeEmptyStructJson(null, xmlin); + log.info(xmlout); + + Assert.assertEquals(xmloutexpected, xmlout); + } + + @Test + public void testRemoveEmptyStructXml() { + String xmlin = "" + + "<T1>\n" + + " <T2>\n" + + " <T3>\n" + + " <T4></T4>\n" + + " <T5> </T5>\n" + + " <T6>\n" + + " </T6>\n" + + " </T3>\n" + + " <T7>blah</T7>\n" + + " </T2>\n" + + " <T8>\n" + + " <T9>\n" + + " <T10></T10>\n" + + " <T11> </T11>\n" + + " <T12>\n" + + " </T12>\n" + + " </T9>\n" + + " <T13>\n" + + " <T14></T14>\n" + + " <T15> </T15>\n" + + " <T16>\n" + + " <T17></T17>\n" + + " </T16>\n" + + " </T13>\n" + + " <T17>\n" + + " </T17>\n" + + " </T8>\n" + + " <T18>blah blah</T18>\n" + + "</T1>\n"; + + String xmloutexpected = "" + + "<T1>\n" + + " <T2>\n" + + " <T7>blah</T7>\n" + + " </T2>\n" + + " <T18>blah blah</T18>\n" + + "</T1>\n"; + + String xmlout = XmlJsonUtil.removeEmptyStructXml(xmlin); + log.info(xmlout); + + Assert.assertEquals(xmloutexpected, xmlout); + } + + @Test + public void testRemoveEmptyStructJson() { + String xmlin = "{\r\n" + + " \"T1\":{\r\n" + + " \"T2\":{\r\n" + + " \"T3\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T4\":{\r\n" + + " \"T12\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T13\":[ ],\r\n" + + " \"T14\":{\r\n" + + " \"T15\":{\r\n" + + " \r\n" + + " },\r\n" + + " \"T16\":{\r\n" + + " \r\n" + + " }\r\n" + + " }\r\n" + + " },\r\n" + + " \"T5\":{\r\n" + + " \"T6\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T7\":[\r\n" + + " \"T8\":{\r\n" + + " \r\n" + + " },\r\n" + + " \"T9\":{ },\r\n" + + " \"T10\":\"blah\",\r\n" + + " \"T11\":[\r\n" + + " \r\n" + + " ]\r\n" + + " ]\r\n" + + " }\r\n" + + " }\r\n" + + " }\r\n" + + "}\r\n" + + ""; + + String xmloutexpected = "{\r\n" + + " \"T1\":{\r\n" + + " \"T2\":{\r\n" + + " \"T5\":{\r\n" + + " \"T7\":[\r\n" + + " \"T10\":\"blah\",\r\n" + + " ]\r\n" + + " }\r\n" + + " }\r\n" + + " }\r\n" + + "}\r\n" + + ""; + + String xmlout = XmlJsonUtil.removeEmptyStructJson(null, xmlin); + log.info(xmlout); + + Assert.assertEquals(xmloutexpected, xmlout); + } +} diff --git a/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java new file mode 100644 index 000000000..76f86abc0 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java @@ -0,0 +1,121 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static org.junit.Assert.assertEquals; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +public class TestXmlParser { + + @Test + public void test() throws Exception { + String path = "src/test/resources/test3.xml"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Set<String> listNameList = new HashSet<String>(); + listNameList.add("project.dependencies.dependency"); + listNameList.add("project.build.plugins.plugin"); + listNameList.add("project.build.plugins.plugin.executions.execution"); + listNameList.add("project.build.pluginManagement.plugins.plugin"); + listNameList.add("project.build.pluginManagement." + + "plugins.plugin.configuration.lifecycleMappingMetadata.pluginExecutions.pluginExecution"); + + Map<String, String> mm = XmlParser.convertToProperties(content, listNameList); + assertEquals("811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VpnId")); + assertEquals("v6", mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.Family")); + assertEquals("SET6_BVOIP_IN", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport")); + assertEquals("AG_MAX_MCASTROUTES", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.ApplyGroup.ApplyGroup")); + assertEquals("ICOREPVC-81114561", mm.get("ApplyGroupResponse.ApplyGroupResponseData.ServiceInstanceId")); + assertEquals("SET_RESET_LP", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport")); + assertEquals("21302:811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfName")); + assertEquals("BGP4_PROTOCOL", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.RoutingProtocol")); + assertEquals("AG6_MAX_PREFIX", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.ApplyGroupPeer.ApplyGroup")); + assertEquals("VPNL811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.End2EndVpnKey")); + assertEquals("AG6_BFD_BGP_3000", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.ApplyGroupNeighbour.ApplyGroup")); + assertEquals("200", mm.get("ApplyGroupResponse.response-code")); + assertEquals("gp6_21302:811182", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.PeerGroupName")); + assertEquals("Y", mm.get("ApplyGroupResponse.ack-final-indicator")); + assertEquals("Success", mm.get("ApplyGroupResponse.response-message")); + } + + @Test + public void testValidLength() throws Exception { + String path = "src/test/resources/test3.xml"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + + Map<String, String> mm = XmlParser.convertToProperties(content, listNameList); + assertEquals("AG6_BFD_BGP_3000", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.ApplyGroupNeighbour.ApplyGroup")); + assertEquals("AG6_MAX_PREFIX", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.ApplyGroupPeer.ApplyGroup")); + assertEquals("v6", mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.Family")); + assertEquals("gp6_21302:811182", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.PeerGroupName")); + assertEquals("BGP4_PROTOCOL", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.RoutingApplyGroups.RoutingProtocol")); + assertEquals("ICOREPVC-81114561", mm.get("ApplyGroupResponse.ApplyGroupResponseData.ServiceInstanceId")); + assertEquals("AG_MAX_MCASTROUTES", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.ApplyGroup.ApplyGroup")); + assertEquals("VPNL811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.End2EndVpnKey")); + assertEquals("811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VpnId")); + assertEquals("SET6_DSU", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[0]")); + assertEquals("SET_DSU", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[1]")); + assertEquals("SET6_MANAGED", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[2]")); + assertEquals("SET_MANAGED", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[3]")); + assertEquals("SET_LOVRF_COMMUNITY", + mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[4]")); + assertEquals("SET_RESET_LP", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[5]")); + assertEquals("6", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport_length")); + assertEquals("SET_BVOIP_IN", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport[0]")); + assertEquals("SET6_BVOIP_IN", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport[1]")); + assertEquals("2", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport_length")); + assertEquals("21302:811182", mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfName")); + assertEquals("Y", mm.get("ApplyGroupResponse.ack-final-indicator")); + assertEquals("200", mm.get("ApplyGroupResponse.response-code")); + assertEquals("Success", mm.get("ApplyGroupResponse.response-message")); + } + + @Test(expected = SvcLogicException.class) + public void testInvalidLength() throws Exception { + String path = "src/test/resources/invalidlength.xml"; + String content = new String(Files.readAllBytes(Paths.get(path))); + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + XmlParser.convertToProperties(content, listNameList); // throws an exception because the length in the xml is + // not a valid number + } + +} diff --git a/plugins/restapi-call-node/provider/src/test/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java b/plugins/restapi-call-node/provider/src/test/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java new file mode 100755 index 000000000..a130d4390 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/org/onap/ccsdk/sli/plugins/restapicall/TestRestapiCallNode.java @@ -0,0 +1,531 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restapicall; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.HashMap; +import java.util.Map; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestRestapiCallNode { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(TestRestapiCallNode.class); + @Rule + public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + + + @Test + public void testDelete() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<>(); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testDeleteWithPayload() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + ctx.setAttribute("prop.name", "site1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/sdwan-site.json"); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "3"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[1].clci", "clci"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[2].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testInvalidRepeatTimes() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "a"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testInvalidTemplatePath() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resourcess/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test(expected = SvcLogicException.class) + public void testWithoutSkipSending() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "false"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + + @Test(expected = SvcLogicException.class) + public void testWithInvalidURI() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("tmp.sdn-circuit-req-row_length", "1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].source-uid", "APIDOC-123"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].action", "delete"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-timestamp", "2016-09-09 16:30:35.0"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].request-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].processing-status", "New"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].service-clfi", "testClfi1"); + ctx.setAttribute("tmp.sdn-circuit-req-row[0].clci", "clci"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/test-template.json"); + p.put("restapiUrl", "http://echo. getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "false"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testVpnJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testSiteJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + ctx.setAttribute("prop.l3vpn.site1_name", "10000000-0000-0000-0000-000000000002"); + ctx.setAttribute("prop.l3vpn.vpn-policy1-id", "10000000-0000-0000-0000-000000000003"); + ctx.setAttribute("prop.l3vpn.entry1-id", "1"); + ctx.setAttribute("prop.l3vpn.sna1_name", "10000000-0000-0000-0000-000000000004"); + ctx.setAttribute("prop.l3vpn.pe1_id", "a8098c1a-f86e-11da-bd1a-00112444be1e"); + ctx.setAttribute("prop.l3vpn.ac1_id", "a8098c1a-f86e-11da-bd1a-00112444be1b"); + ctx.setAttribute("prop.l3vpn.ac1-peer-ip", "192.168.1.1"); + ctx.setAttribute("prop.l3vpn.ac1-ip", "192.168.1.2"); + ctx.setAttribute("prop.l3vpn.sna1_svlan", "100"); + ctx.setAttribute("prop.l3vpn.ac1_protocol", "static"); + ctx.setAttribute("prop.l3vpn.sna1-route.ip-prefix", "192.168.1.1/24"); + ctx.setAttribute("prop.l3vpn.sna1-route.next-hop", "192.168.1.4"); + + ctx.setAttribute("prop.l3vpn.site2_name", "10000000-0000-0000-0000-000000000005"); + ctx.setAttribute("prop.l3vpn.vpn-policy2-id", "10000000-0000-0000-0000-000000000006"); + ctx.setAttribute("prop.l3vpn.entry2-id", "1"); + ctx.setAttribute("prop.l3vpn.sna2_name", "10000000-0000-0000-0000-000000000007"); + ctx.setAttribute("prop.l3vpn.pe2_id", "a8098c1a-f86e-11da-bd1a-00112444be1a"); + ctx.setAttribute("prop.l3vpn.ac2_id", "a8098c1a-f86e-11da-bd1a-00112444be1c"); + ctx.setAttribute("prop.l3vpn.ac2-peer-ip", "192.168.1.6"); + ctx.setAttribute("prop.l3vpn.ac2-ip", "192.168.1.5"); + ctx.setAttribute("prop.l3vpn.sna2_svlan", "200"); + ctx.setAttribute("prop.l3vpn.ac2_protocol", "bgp"); + ctx.setAttribute("prop.l3vpn.peer2-ip", "192.168.1.7"); + ctx.setAttribute("prop.l3vpn.ac2_protocol_bgp_as", "200"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smsitetemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/sites"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testVrfJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.vrf1-id", "10000000-0000-0000-0000-000000000007"); + ctx.setAttribute("prop.l3vpn.vpn-policy1-id", "10000000-0000-0000-0000-000000000003"); + ctx.setAttribute("prop.l3vpn.pe1_id", "a8098c1a-f86e-11da-bd1a-00112444be1e"); + ctx.setAttribute("prop.l3vpn.vrf2-id", "10000000-0000-0000-0000-000000000009"); + ctx.setAttribute("prop.l3vpn.vpn-policy2-id", "10000000-0000-0000-0000-000000000006"); + ctx.setAttribute("prop.l3vpn.pe2_id", "a8098c1a-f86e-11da-bd1a-00112444be1a"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvrftemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vrf-attributes"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testDeleteVpnJsonTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + //p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services" + + "/vpnservice=10000000-0000-0000-0000-000000000001"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "json"); + p.put("httpMethod", "delete"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testL2DciTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.dci-connects.id", "Id1"); + ctx.setAttribute("prop.dci-connects.name", "Name1"); + ctx.setAttribute("prop.dci-connects.local_networks[0]", "NetId1"); + ctx.setAttribute("prop.dci-connects.local_networks[1]", "NetId2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[0]", "100:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[0]", "100:2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[1]", "200:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[1]", "200:2"); + ctx.setAttribute("prop.dci-connects.vni", "1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l2-dci-connects-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testL3DciTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.dci-connects.id", "Id1"); + ctx.setAttribute("prop.dci-connects.name", "Name1"); + ctx.setAttribute("prop.dci-connects.local_networks_length", "2"); + ctx.setAttribute("prop.dci-connects.local_networks[0]", "NetId1"); + ctx.setAttribute("prop.dci-connects.local_networks[1]", "NetId2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[0]", "100:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[0]", "100:2"); + ctx.setAttribute("prop.dci-connects.evpn_irts[1]", "200:1"); + ctx.setAttribute("prop.dci-connects.evpn_erts[1]", "200:2"); + ctx.setAttribute("prop.dci-connects.vni", "1"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3-dci-connects-template.json"); + p.put("restapiUrl", "http://echo.getpostman.com"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + + } + + @Test + public void testControllerTokenTemplate() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.sdncRestApi.thirdpartySdnc.user", "admin"); + ctx.setAttribute("prop.sdncRestApi.thirdpartySdnc.password", "admin123"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/actokentemplate.json"); + p.put("restapiUrl", "https://ipwan:18002/controller/v2/tokens"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + + @Test + public void testDeleteNoneAsContentType() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + + Map<String, String> p = new HashMap<>(); + p.put("restapiUrl", "https://echo.getpostman.com/delete"); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "pwd1"); + p.put("httpMethod", "delete"); + p.put("format", "none"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + + @Test + public void testPostNoneAsContentType() throws SvcLogicException { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.l3vpn.name", "10000000-0000-0000-0000-000000000001"); + ctx.setAttribute("prop.l3vpn.topology", "point_to_point"); + + Map<String, String> p = new HashMap<>(); + p.put("templateFileName", "src/test/resources/l3smvpntemplate.json"); + p.put("restapiUrl", "http://ipwan:18002/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services"); + p.put("restapiUser", "admin"); + p.put("restapiPassword", "admin123"); + p.put("format", "none"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "restapi-result"); + p.put("skipSending", "true"); + + RestapiCallNode rcn = new RestapiCallNode(); + rcn.sendRequest(p, ctx); + } + /* + * { + "partnerOne": { + "url": "http://localhost:7001" 4 http://uebsb93kcdc.it.att.com:3904", + "test": "/metrics" + }, + "partnerTwo": { + "url": "http://localhost:7002", + "user": "controller_user", + "password": "P@ssword", + "test": "/metrics" + }, + "partnerThree": { + "url": "http://localhost:7003", + "user": "controller_admin" + } +} + */ + @Test + public void testPartners() throws Exception{ + + environmentVariables.set("deployer_pass", "sdncp-123"); + assertEquals("sdncp-123", System.getenv("deployer_pass")); + + String partnerTwoKey = "partnerTwo"; + String partnerTwoUsername = "controller_user"; + String partnerTwoPassword = "P@ssword"; + + System.setProperty("SDNC_CONFIG_DIR", "src/test/resources"); + RestapiCallNode rcn = new RestapiCallNode(); + assertNull(rcn.partnerStore.get("partnerOne")); + PartnerDetails details = rcn.partnerStore.get(partnerTwoKey); + assertEquals(partnerTwoUsername,details.username); + assertEquals(partnerTwoPassword,details.password); + assertNull(rcn.partnerStore.get("partnerThree")); + + //In this scenario the caller expects username, password and url to be picked up from the partners json + Map<String, String> paramMap = new HashMap<String,String>(); + paramMap.put("partner", partnerTwoKey); + rcn.handlePartner(paramMap ); + assertEquals(partnerTwoUsername,paramMap.get(rcn.restapiUserKey)); + assertEquals(partnerTwoPassword,paramMap.get(rcn.restapiPasswordKey)); + assertEquals("http://localhost:7002",paramMap.get(rcn.restapiUrlString)); + + //In this scenario the caller expects username, password and url to be picked up from the partners json + //the provided suffix will be appended to the default url from the partners json + paramMap = new HashMap<String,String>(); + paramMap.put("partner", partnerTwoKey); + paramMap.put("restapiUrlSuffix", "/networking/v1/instance/3"); + rcn.handlePartner(paramMap); + p = new Parameters(); + RestapiCallNode.getParameters(paramMap, p); + assertEquals(partnerTwoUsername,p.restapiUser); + assertEquals(partnerTwoPassword,p.restapiPassword); + assertEquals("http://localhost:7002/networking/v1/instance/3",p.restapiUrl); + + paramMap = new HashMap<String, String>(); + paramMap.put("partner","partnerFour" ); + paramMap.put("httpMethod", "delete"); + paramMap.put("skipSending", "true"); + rcn.handlePartner(paramMap); + Parameters p = new Parameters(); + RestapiCallNode.getParameters(paramMap, p); + assertEquals(p.restapiPassword, "sdncp-123"); + assertEquals(p.restapiUser, "m30402@sdncp.att.com"); + assertEquals(p.restapiUrl, "http://localhost:7004"); + } + + @Test + public void retryPolicyBean() throws Exception { + Integer retries = 3; + String first = "http://localhost:7001"; + String second = "http://localhost:7001"; + + RetryPolicy p = new RetryPolicy(new String[] {first,second}, retries); + assertEquals(retries,p.getMaximumRetries()); + assertNotNull(p.getRetryMessage()); + String next = p.getNextHostName(); + assertEquals(second,next); + assertEquals(1,p.getRetryCount()); + next = p.getNextHostName(); + assertEquals(first,next); + assertEquals(2,p.getRetryCount()); + } +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/1dArray.json b/plugins/restapi-call-node/provider/src/test/resources/1dArray.json new file mode 100644 index 000000000..208fb70a6 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/1dArray.json @@ -0,0 +1,8 @@ +[
+ "apple",
+ "orange",
+ "banana",
+ "squash",
+ "broccoli",
+ "cauliflower"
+]
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/2dArray.json b/plugins/restapi-call-node/provider/src/test/resources/2dArray.json new file mode 100644 index 000000000..b473864d8 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/2dArray.json @@ -0,0 +1,4 @@ +[
+ ["apple", "orange", "banana"],
+ ["squash", "broccoli", "cauliflower"]
+]
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/3dArray.json b/plugins/restapi-call-node/provider/src/test/resources/3dArray.json new file mode 100644 index 000000000..3f3f4fab8 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/3dArray.json @@ -0,0 +1,5 @@ +[
+ [["x","y","z"]],
+ [["abc","def","xyz"], [123, 456, 789]],
+ [["a","b","c"], ["d","e","f"], ["1","2","3"]]
+]
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/ArrayMenu.json b/plugins/restapi-call-node/provider/src/test/resources/ArrayMenu.json new file mode 100644 index 000000000..b12f16315 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/ArrayMenu.json @@ -0,0 +1,41 @@ +[{
+ "id": "1",
+ "type": "pizza",
+ "name": "plain",
+ "calories": 1000,
+ "vegetarian": true
+ }, {
+ "id": "2",
+ "type": "pizza",
+ "name": "Tuesday Special",
+ "calories": 2000,
+ "vegetarian": false,
+ "topping":
+ [{
+ "id": "1",
+ "name": "onion"
+ }, {
+ "id": "2",
+ "name": "pepperoni"
+ }
+ ]
+ }, {
+ "id": "3",
+ "type": "pizza",
+ "name": "House Special",
+ "calories": 1500,
+ "vegetarian": true,
+ "topping":
+ [{
+ "id": "3",
+ "name": "basil"
+ }, {
+ "id": "4",
+ "name": "fresh mozzarella"
+ }, {
+ "id": "5",
+ "name": "tomato"
+ }
+ ]
+ }
+]
diff --git a/plugins/restapi-call-node/provider/src/test/resources/EmbeddedEscapedJson.json b/plugins/restapi-call-node/provider/src/test/resources/EmbeddedEscapedJson.json new file mode 100644 index 000000000..dbb6d8d3a --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/EmbeddedEscapedJson.json @@ -0,0 +1,16 @@ +{
+ "input": {
+ "parameters":
+ [{
+ "name": "escapedJsonObject",
+ "value": "[{\"id\":\"0.2.0.0\/16\"},{\"id\":\"ge04::\/64\"}]"
+ }, {
+ "name": "password",
+ "value": "Hello\/World"
+ }, {
+ "name": "resourceName",
+ "value": "The\t\"Best\"\tName"
+ }
+ ]
+ }
+}
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/EscapedJson.json b/plugins/restapi-call-node/provider/src/test/resources/EscapedJson.json new file mode 100644 index 000000000..a7719e819 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/EscapedJson.json @@ -0,0 +1 @@ +{\"widget\":{\"debug\":false,\"window\":{\"title\":\"ONAP Widget\",\"name\":\"main_window\",\"width\":200,\"height\":300},\"image\":{\"src\":\"images\/moon.png\",\"name\":\"moon\",\"hOffset\":150,\"vOffset\":150,\"alignment\":\"center\"},\"text\":{\"data\":\"Click Me\",\"size\":21,\"style\":\"bold\",\"name\":\"text1\",\"hOffset\":350,\"vOffset\":200,\"alignment\":\"center\"}}}
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/ObjectMenu.json b/plugins/restapi-call-node/provider/src/test/resources/ObjectMenu.json new file mode 100644 index 000000000..56f842d48 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/ObjectMenu.json @@ -0,0 +1,43 @@ +{
+ "menu": [{
+ "id": "1",
+ "type": "pizza",
+ "name": "plain",
+ "calories": 1000,
+ "vegetarian": true
+ }, {
+ "id": "2",
+ "type": "pizza",
+ "name": "Tuesday Special",
+ "calories": 2000,
+ "vegetarian": false,
+ "topping":
+ [{
+ "id": "1",
+ "name": "onion"
+ }, {
+ "id": "2",
+ "name": "pepperoni"
+ }
+ ]
+ }, {
+ "id": "3",
+ "type": "pizza",
+ "name": "House Special",
+ "calories": 1500,
+ "vegetarian": true,
+ "topping":
+ [{
+ "id": "3",
+ "name": "basil"
+ }, {
+ "id": "4",
+ "name": "fresh mozzarella"
+ }, {
+ "id": "5",
+ "name": "tomato"
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins/restapi-call-node/provider/src/test/resources/Widget.json b/plugins/restapi-call-node/provider/src/test/resources/Widget.json new file mode 100644 index 000000000..1e25282c2 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/Widget.json @@ -0,0 +1,27 @@ +{
+ "widget": {
+ "debug": false,
+ "window": {
+ "title": "ONAP Widget",
+ "name": "main_window",
+ "width": 200,
+ "height": 300
+ },
+ "image": {
+ "src": "images/moon.png",
+ "name": "moon",
+ "hOffset": 150,
+ "vOffset": 150,
+ "alignment": "center"
+ },
+ "text": {
+ "data": "Click Me",
+ "size": 21,
+ "style": "bold",
+ "name": "text1",
+ "hOffset": 350,
+ "vOffset": 200,
+ "alignment": "center"
+ }
+ }
+}
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/actokentemplate.json b/plugins/restapi-call-node/provider/src/test/resources/actokentemplate.json new file mode 100644 index 000000000..02e62400f --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/actokentemplate.json @@ -0,0 +1,4 @@ +{ + "userName": ${prop.sdncRestApi.thirdpartySdnc.user}, + "password": ${prop.sdncRestApi.thirdpartySdnc.password} +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/invalidlength.xml b/plugins/restapi-call-node/provider/src/test/resources/invalidlength.xml new file mode 100644 index 000000000..c086d564e --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/invalidlength.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport_length>a</VrfExport_length> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/restapi-call-node/provider/src/test/resources/l2-dci-connects-template.json b/plugins/restapi-call-node/provider/src/test/resources/l2-dci-connects-template.json new file mode 100644 index 000000000..e0896bbaa --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/l2-dci-connects-template.json @@ -0,0 +1,11 @@ +{ + "l2-dci-connect": { + "id": ${prop.dci-connects.id}, + "name": ${prop.dci-connects.name}, + "description": ${prop.dci-connects.description}, + "network_id": ${prop.dci-connects.local_networks[0]}, + "evpn_irts": ${prop.dci-connects.evpn_irts}, + "evpn_erts": ${prop.dci-connects.evpn_erts}, + "vni": ${prop.dci-connects.vni} + } +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/l3-dci-connects-template.json b/plugins/restapi-call-node/provider/src/test/resources/l3-dci-connects-template.json new file mode 100644 index 000000000..41df794fd --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/l3-dci-connects-template.json @@ -0,0 +1,14 @@ +{ + "l3-dci-connect": { + "id": ${prop.dci-connects.id}, + "name": ${prop.dci-connects.name}, + "description": ${prop.dci-connects.description}, + "router_id": ${prop.dci-connects.router_id}, + "firewall_enable": false, + "local_networks": ${prop.dci-connects.local_networks}, + "local_network_all": false, + "evpn_irts": ${prop.dci-connects.evpn_irts}, + "evpn_erts": ${prop.dci-connects.evpn_erts}, + "l3_vni": ${prop.dci-connects.vni} + } +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/l3smsitetemplate.json b/plugins/restapi-call-node/provider/src/test/resources/l3smsitetemplate.json new file mode 100644 index 000000000..8f8156698 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/l3smsitetemplate.json @@ -0,0 +1,162 @@ +{ + "site":[ + { + "site-id": ${prop.l3vpn.site1_name}, + "vpn-policies": { + "vpn-policy": [ + { + "vpn-policy-id": ${prop.l3vpn.vpn-policy1-id}, + "entries": [ + { + "id": ${prop.l3vpn.entry1-id}, + "vpn": { + "vpn-id": ${prop.l3vpn.name}, + "site-role": "huawei-ac-net-l3vpn-svc:any-to-any-role" + } + } + ] + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": ${prop.l3vpn.sna1_name}, + "bearer": { + "appointed-access": { + "ne-id": ${prop.l3vpn.pe1_id}, + "ltp-id": ${prop.l3vpn.ac1_id}, + "vlan": { + "type": "huawei-ac-net-l3vpn-svc:vlan-dot1q", + "vlan-id": ${prop.l3vpn.sna1_svlan} + } + } + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "huawei-ac-net-l3vpn-svc:static-address", + "addresses": { + "provider-address": ${prop.l3vpn.ac1-peer-ip}, + "customer-address": ${prop.l3vpn.ac1-ip}, + "mask": "24" + } + } + }, + "service": { + "mtu": "1500" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": ${prop.l3vpn.ac1_protocol}, + "static": { + "cascaded-lan-prefixes": [ + { + "ipv4-lan-prefixes": [ + { + "ip-prefix": ${prop.l3vpn.sna1-route.ip-prefix}, + "next-hop": ${prop.l3vpn.sna1-route.next-hop} + } + ] + } + ] + } + "bgp": { + "peers": [ + { + "peer-ip": ${prop.l3vpn.peer1-ip}, + "remote-as": ${prop.l3vpn.ac1_protocol_bgp_as} + } + ] + } + } + } + ] + }, + "vpn-attachment": { + "vpn-policy-id": ${prop.l3vpn.name} + } + } + ] + }, + { + "site-id": ${prop.l3vpn.site2_name}, + "vpn-policies": { + "vpn-policy": [ + { + "vpn-policy-id": ${prop.l3vpn.vpn-policy2-id}, + "entries": [ + { + "id": ${prop.l3vpn.entry2-id}, + "vpn": { + "vpn-id": ${prop.l3vpn.name}, + "site-role": "huawei-ac-net-l3vpn-svc:any-to-any-role" + } + } + ] + } + ] + }, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": ${prop.l3vpn.sna2_name}, + "bearer": { + "appointed-access": { + "ne-id": ${prop.l3vpn.pe2_id}, + "ltp-id": ${prop.l3vpn.ac2_id}, + "vlan": { + "type": "huawei-ac-net-l3vpn-svc:vlan-dot1q", + "vlan-id": ${prop.l3vpn.sna2_svlan} + } + } + }, + "ip-connection": { + "ipv4": { + "address-allocation-type": "huawei-ac-net-l3vpn-svc:static-address", + "addresses": { + "provider-address": ${prop.l3vpn.ac2-peer-ip}, + "customer-address": ${prop.l3vpn.ac2-ip}, + "mask": "24" + } + } + }, + "service": { + "mtu": "1500" + }, + "routing-protocols": { + "routing-protocol": [ + { + "type": ${prop.l3vpn.ac2_protocol}, + "static": { + "cascaded-lan-prefixes": [ + { + "ipv4-lan-prefixes": [ + { + "ip-prefix": ${prop.l3vpn.sna2-route.ip-prefix}, + "next-hop": ${prop.l3vpn.sna2-route.next-hop} + } + ] + } + ] + } + "bgp": { + "peers": [ + { + "peer-ip": ${prop.l3vpn.peer2-ip}, + "remote-as": ${prop.l3vpn.ac2_protocol_bgp_as} + } + ] + } + } + ] + }, + "vpn-attachment": { + "vpn-policy-id": ${prop.l3vpn.name} + } + } + ] + } + ] +} + diff --git a/plugins/restapi-call-node/provider/src/test/resources/l3smvpntemplate.json b/plugins/restapi-call-node/provider/src/test/resources/l3smvpntemplate.json new file mode 100644 index 000000000..30f30a24f --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/l3smvpntemplate.json @@ -0,0 +1,10 @@ +{ + "vpn-service": [ + { + "vpn-id": ${prop.l3vpn.name}, + "customer-name": "huawei", + "vpn-service-topology": ${prop.l3vpn.topology}, + "route-exchange-policy": "huawei-ac-net-l3vpn-svc:vpnv4-way" + } + ] +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/l3smvrftemplate.json b/plugins/restapi-call-node/provider/src/test/resources/l3smvrftemplate.json new file mode 100644 index 000000000..52359a7fd --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/l3smvrftemplate.json @@ -0,0 +1,14 @@ +{ + "vrf-attribute": [ + { + "vrf-attribute-id": ${prop.l3vpn.vrf1-id}, + "ne-id": ${prop.l3vpn.pe1_id}, + "vpn-policy-id": ${prop.l3vpn.vpn-policy1-id} + }, + { + "vrf-attribute-id": ${prop.l3vpn.vrf2-id}, + "ne-id": ${prop.l3vpn.pe2_id}, + "vpn-policy-id": ${prop.l3vpn.vpn-policy2-id} + } + ] +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/partners.json b/plugins/restapi-call-node/provider/src/test/resources/partners.json new file mode 100755 index 000000000..2562d69c0 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/partners.json @@ -0,0 +1,22 @@ +{
+ "partnerOne": {
+ "url": "http://localhost:7001",
+ "test": "/metrics"
+ },
+ "partnerTwo": {
+ "url": "http://localhost:7002",
+ "user": "controller_user",
+ "password": "P@ssword",
+ "test": "/metrics"
+ },
+ "partnerThree": {
+ "url": "http://localhost:7003",
+ "user": "controller_admin"
+ },
+ "partnerFour": {
+ "url": "http://localhost:7004",
+ "user": "m30402@sdncp.att.com",
+ "password": "${deployer_pass}",
+ "test": "/metrics"
+ }
+}
diff --git a/plugins/restapi-call-node/provider/src/test/resources/sdwan-site.json b/plugins/restapi-call-node/provider/src/test/resources/sdwan-site.json new file mode 100644 index 000000000..238ec7642 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/sdwan-site.json @@ -0,0 +1,10 @@ +{ + "sites": [ + { + "name": ${prop.name}, + "controlPoints": [] + } + ] +} + + diff --git a/plugins/restapi-call-node/provider/src/test/resources/sdwan-vpn-topology.json b/plugins/restapi-call-node/provider/src/test/resources/sdwan-vpn-topology.json new file mode 100644 index 000000000..4260d2fc1 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/sdwan-vpn-topology.json @@ -0,0 +1,23 @@ +{ + "vpnId": ${prop.vpn1Id}, + "areas": [ + { + "name": "area1", + "areaType": "leaf", + "topologyType": ${prop.topology}, + "siteAttachements": [${repeat:prop.siteAttachement_length: + { + "siteId": ${prop.siteAttachement[${1}].siteId}, + "role": [${repeat:prop.siteAttachement[${1}].roles_length: + ${prop.siteAttachement[${1}].roles[${2}]}, + } + ], + "workMode": "with-interconnection", + "sitePriority": "active", + "borders": "null" + }, + } + ] + } + ] +}
\ No newline at end of file diff --git a/plugins/restapi-call-node/provider/src/test/resources/test-template.json b/plugins/restapi-call-node/provider/src/test/resources/test-template.json new file mode 100644 index 000000000..ae519a731 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/test-template.json @@ -0,0 +1,37 @@ +{ + "sdn-circuit-request": [ + ${repeat:tmp.sdn-circuit-req-row_length: + { + "request-id": ${tmp.sdn-circuit-req-row[${1}].request-id}, + "source-uid": ${tmp.sdn-circuit-req-row[${1}].source-uid}, + "action": ${tmp.sdn-circuit-req-row[${1}].action}, + "request-timestamp": ${tmp.sdn-circuit-req-row[${1}].request-timestamp}, + "update-timestamp": ${tmp.sdn-circuit-req-row[${1}].update-timestamp}, + "request-status": ${tmp.sdn-circuit-req-row[${1}].request-status}, + "processing-status": ${tmp.sdn-circuit-req-row[${1}].processing-status}, + "reason-code": ${tmp.sdn-circuit-req-row[${1}].reason-code}, + "reason-message": ${tmp.sdn-circuit-req-row[${1}].reason-message}, + "customer-code": ${tmp.sdn-circuit-req-row[${1}].customer-code}, + "bundle-id": ${tmp.sdn-circuit-req-row[${1}].bundle-id}, + "router-name-1": ${tmp.sdn-circuit-req-row[${1}].router-name-1}, + "tail-clfi-1": ${tmp.sdn-circuit-req-row[${1}].tail-clfi-1}, + "srg-1": ${tmp.sdn-circuit-req-row[${1}].srg-1}, + "router-name-2": ${tmp.sdn-circuit-req-row[${1}].router-name-2}, + "tail-clfi-2": ${tmp.sdn-circuit-req-row[${1}].tail-clfi-2}, + "srg-2": ${tmp.sdn-circuit-req-row[${1}].srg-2}, + "facility-speed": ${tmp.sdn-circuit-req-row[${1}].facility-speed}, + "facility-speed-units": ${tmp.sdn-circuit-req-row[${1}].facility-speed-units}, + "facility-type": ${tmp.sdn-circuit-req-row[${1}].facility-type}, + "service-clfi": ${tmp.sdn-circuit-req-row[${1}].service-clfi}, + "clci": ${tmp.sdn-circuit-req-row[${1}].clci}, + "wavelength-purpose": ${tmp.sdn-circuit-req-row[${1}].wavelength-purpose}, + "activate-setting": ${tmp.sdn-circuit-req-row[${1}].activate-setting}, + "apply-date": ${tmp.sdn-circuit-req-row[${1}].apply-date}, + "run-id": ${tmp.sdn-circuit-req-row[${1}].run-id}, + "hostname": ${tmp.sdn-circuit-req-row[${1}].hostname}, + "algo-request-reason": ${tmp.sdn-circuit-req-row[${1}].algo-request-reason} + "test-empty-value": ${tmp.sdn-circuit-req-row[${1}].test-empty-value} + }, + } + ] +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/test.json b/plugins/restapi-call-node/provider/src/test/resources/test.json new file mode 100644 index 000000000..75155393c --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/test.json @@ -0,0 +1,30 @@ +{ + "equipment-data": [ + { + "server-count": "4", + "max-server-speed": "1600000", + "number-primary-servers": "2", + "equipment-id": "Server1", + "server-model": "Unknown", + "server-id": "Server1", + "test-node" : { + "test-inner-node" : "Test-Value" + } + } + ], + "resource-state": { + "threshold-value": "1600000", + "last-added": "1605000", + "used": "1605000", + "limit-value": "1920000" + }, + "resource-rule": { + "endpoint-position": "VCE-Cust", + "soft-limit-expression": "0.6 * max-server-speed * number-primary-servers", + "resource-name": "Bandwidth", + "service-model": "DUMMY", + "hard-limit-expression": "max-server-speed * number-primary-servers", + "equipment-level": "Server" + }, + "message": "The provisioned access bandwidth is at or exceeds 50% of the total server capacity." +} diff --git a/plugins/restapi-call-node/provider/src/test/resources/test.xml b/plugins/restapi-call-node/provider/src/test/resources/test.xml new file mode 100644 index 000000000..1974f688e --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/test.xml @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node</artifactId> + <version>6.0.0-SNAPSHOT</version> + </parent> + <artifactId>restapi-call-node-provider</artifactId> + <packaging>bundle</packaging> + <name>RESTAPI Call Node - Provider</name> + <url>http://maven.apache.org</url> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>3.1.4.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-common</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-provider</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-beans</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>xerces</groupId> + <artifactId>xerces</artifactId> + <version>2.4.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-signature</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.brocade.developer</groupId> + <artifactId>providermodule-plugin</artifactId> + <configuration> + <packageId>org.onap.ccsdk.sli.plugins</packageId> + <appName>restapi-call-node</appName> + </configuration> + <executions> + <execution> + <phase>process-sources</phase> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>org.onap.ccsdk.sli.plugins.restapicall</Bundle-SymbolicName> + <Export-Package>org.onap.ccsdk.sli.plugins.restapicall</Export-Package> + <Import-Package>*</Import-Package> + </instructions> + + <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation> + + </configuration> + + </plugin> + + + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + com.brocade.developer + </groupId> + <artifactId> + providermodule-plugin + </artifactId> + <versionRange> + [1.2.0.100-SNAPSHOT,) + </versionRange> + <goals> + <goal>process</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/plugins/restapi-call-node/provider/src/test/resources/test3.xml b/plugins/restapi-call-node/provider/src/test/resources/test3.xml new file mode 100644 index 000000000..ade412681 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/test3.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v4</Family> + <PeerGroupName>gp_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v6</Family> + <PeerGroupName>gp6_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/restapi-call-node/provider/src/test/resources/testEmbeddedTemplate.json b/plugins/restapi-call-node/provider/src/test/resources/testEmbeddedTemplate.json new file mode 100644 index 000000000..7f2377813 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/testEmbeddedTemplate.json @@ -0,0 +1,20 @@ +{ + "commonHeader": { + "origin": "earth", + "requestId": ${reqId}, + "subRequestId": ${subReqId} + }, + "actions": { + "actionName": ${actionName}, + "mode": "sync" + }, + "payload": { + "assignment-request": { + "prefix": [ + ${myPrefix} + ], + "assignment-properties": ${'"complexObj} + } + } +} + diff --git a/plugins/restapi-call-node/provider/src/test/resources/testMultiLineEmbeddedTemplate.json b/plugins/restapi-call-node/provider/src/test/resources/testMultiLineEmbeddedTemplate.json new file mode 100644 index 000000000..84ae3a492 --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/testMultiLineEmbeddedTemplate.json @@ -0,0 +1,20 @@ +{ + "commonHeader": { + "origin": "earth", + "requestId": ${reqId}, + "subRequestId": ${subReqId} + }, + "actions": { + "actionName": ${actionName}, + "mode": "sync" + }, + "payload": { + "assignment-request": { + "prefix": [ + ${myPrefix} + ], + "assignment-properties": ${complexObj} + } + } +} + diff --git a/plugins/restapi-call-node/provider/src/test/resources/test_file.txt b/plugins/restapi-call-node/provider/src/test/resources/test_file.txt new file mode 100644 index 000000000..038d757dc --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/test_file.txt @@ -0,0 +1,5 @@ +“You think your pain and your heartbreak are unprecedented in the history of the world, but then you read. It was books that taught me that the things that tormented me most were the very things that connected me with all the people who were alive, who had ever been alive.” – James Baldwin +“When I have a little money, I buy books; and if I have any left, I buy food and clothes.” – Erasmus +“If you only read the books that everyone else is reading, you can only think what everyone else is thinking.” – Haruki Murakami +“You don’t have to burn books to destroy a culture. Just get people to stop reading them.” – Ray Bradbury +“A reader lives a thousand lives before he dies, said Jojen. The man who never reads lives only one.” – George R.R. Martin diff --git a/plugins/restapi-call-node/provider/src/test/resources/ueb.properties b/plugins/restapi-call-node/provider/src/test/resources/ueb.properties new file mode 100644 index 000000000..96657ee1a --- /dev/null +++ b/plugins/restapi-call-node/provider/src/test/resources/ueb.properties @@ -0,0 +1,6 @@ +#for other servers see http://sa2020.it.att.com:8888/sw/cambria/installs + +#to check connectivity http://hostname:3904/metrics + +servers=http://uebsb91kcdc.it.att.com:3904 http://uebsb92kcdc.it.att.com:3904 http://uebsb93kcdc.it.att.com:3904 + diff --git a/plugins/restconf-client/.gitignore b/plugins/restconf-client/.gitignore new file mode 100755 index 000000000..b73caf31e --- /dev/null +++ b/plugins/restconf-client/.gitignore @@ -0,0 +1,34 @@ +#####standard .git ignore entries##### + +## IDE Specific Files ## +org.eclipse.core.resources.prefs +.classpath +.project +.settings +.idea +.externalToolBuilders +maven-eclipse.xml +workspace + +## Compilation Files ## +*.class +**/target +target +target-ide +MANIFEST.MF + +## Misc Ignores (OS specific etc) ## +bin/ +dist +*~ +*.ipr +*.iml +*.iws +classes +out/ +.DS_STORE +.metadata + +## Folders which contain auto generated source code ## +yang-gen-config +yang-gen-sal diff --git a/plugins/restconf-client/installer/pom.xml b/plugins/restconf-client/installer/pom.xml new file mode 100755 index 000000000..9114c7a22 --- /dev/null +++ b/plugins/restconf-client/installer/pom.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: restconf-client :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-restconf-client</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features + </features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/restconf-client/installer/src/assembly/assemble_installer_zip.xml b/plugins/restconf-client/installer/src/assembly/assemble_installer_zip.xml new file mode 100644 index 000000000..8ffcb88a1 --- /dev/null +++ b/plugins/restconf-client/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,58 @@ +<!-- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/restconf-client/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/restconf-client/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..252c39d3c --- /dev/null +++ b/plugins/restconf-client/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,48 @@ +<!-- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/restconf-client/installer/src/main/resources/scripts/install-feature.sh b/plugins/restconf-client/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..8e9195d57 --- /dev/null +++ b/plugins/restconf-client/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# ONAP : CCSDK +# ================================================================================ +# Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -nd ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/restconf-client/pom.xml b/plugins/restconf-client/pom.xml new file mode 100755 index 000000000..ee2d0423c --- /dev/null +++ b/plugins/restconf-client/pom.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: restconf-client</name> + <description>This is an implementation of DG Execute Node that makes a call to an external RESTCONF API</description> + + <modules> + <module>provider</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/restconf-client/provider/pom.xml b/plugins/restconf-client/provider/pom.xml new file mode 100755 index 000000000..ab57af017 --- /dev/null +++ b/plugins/restconf-client/provider/pom.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restconf-client-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: restconf-client :: ${project.artifactId}</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-core-artifacts</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-sse</artifactId> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-provider</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>properties-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-data-impl</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>org.dom4j</groupId> + <artifactId>dom4j</artifactId> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>restapi-call-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-nb-rfc8040</artifactId> + <scope>provided</scope> + <exclusions> + <exclusion> + <groupId>javax.xml</groupId> + <artifactId>jsr173</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-parser-impl</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <scope>provided</scope> + </dependency> + + <!-- For test --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-servlet</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-grizzly2-http</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-test-util</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.core</groupId> + <artifactId>jersey-client</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java new file mode 100644 index 000000000..620df282f --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiCallNode.java @@ -0,0 +1,484 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfapicall; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.stream.JsonWriter; + +import javax.ws.rs.core.UriBuilder; +import java.io.StringWriter; +import java.io.Writer; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.plugins.restapicall.Format; +import org.onap.ccsdk.sli.plugins.restapicall.HttpResponse; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restapicall.XmlParser; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatSerializer; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatSerializerContext; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerFactory; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.Listener; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.MdsalSerializerHelper; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.SerializerHelper; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeSerializer; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeSerializer; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Strings.repeat; +import static java.lang.String.format; +import static java.lang.String.valueOf; +import static org.apache.commons.lang3.StringUtils.join; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.DELETE; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.GET; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PATCH; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.ATTEMPTS_MSG; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COLON; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMMA; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.COMM_FAIL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HEADER; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HTTP_REQ; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.HTTP_RES; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.MAX_RETRY_ERR; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.NO_MORE_RETRY; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.REQ_ERR; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.REST_API_URL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_CODE; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_MSG; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RES_PRE; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_COUNT; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.RETRY_FAIL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.UPDATED_URL; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getSchemaCtxFromDir; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getUpdatedXmlReq; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.getYangParameters; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.parseUrl; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfListenerFactory.instance; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.FORMAT_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.UTF_HEADER; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.XML_TREE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.getXmlWriter; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getModuleNamespace; +import static org.osgi.framework.FrameworkUtil.getBundle; + +/** + * Representation of a plugin to enable RESTCONF based CRUD operations from DG. + */ +public class RestconfApiCallNode implements SvcLogicJavaPlugin { + + /** + * Logger for the restconf api call node class. + */ + private static final Logger log = LoggerFactory.getLogger( + RestconfApiCallNode.class); + + /** + * Rest api call node service instance + */ + private RestapiCallNode restapiCallNode; + + /** + * Creates an instance of restconf api call node with restapi call node. + * + * @param r restapi call node + */ + public RestconfApiCallNode(RestapiCallNode r) { + this.restapiCallNode = r; + } + + /** + * Returns the restapi call node instance. + * @return + */ + public RestapiCallNode getRestapiCallNode() { + return restapiCallNode; + } + + /** + * Sends the restconf request using the parameters map and the memory + * context. And this method allows the directed graphs to interact with + * the restconf api call node + * + * @param paramMap parameters map + * @param ctx service logic context + * @throws SvcLogicException when svc logic exception occurs + */ + public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx) + throws SvcLogicException { + sendRequest(paramMap, ctx, 0); + } + + /** + * Sends the restconf request using the parameters map and the memory + * context along with the retry count. + * + * @param paramMap parameters map + * @param ctx service logic context + * @param retryCount number of retry counts + * @throws SvcLogicException when svc logic exception occurs + */ + public void sendRequest(Map<String, String> paramMap, SvcLogicContext ctx, + Integer retryCount) throws SvcLogicException { + RestapiCallNode rest = getRestapiCallNode(); + HttpResponse r = new HttpResponse(); + try { + YangParameters p = getYangParameters(paramMap); + + String pp = p.responsePrefix != null ? p.responsePrefix + '.' : ""; + Map<String, String> props = new HashMap<>((Map)ctx.toProperties()); + String uri = parseUrl(p.restapiUrl, p.httpMethod); + InstanceIdentifierContext<?> insIdCtx = getInsIdCtx(p, uri); + + String req = null; + if (p.httpMethod != GET && p.httpMethod != DELETE) { + req = serializeRequest(props, p, uri, insIdCtx); + if (p.httpMethod == PUT || p.httpMethod == PATCH) { + updateReq(req, p, insIdCtx); + } + } + if (req == null && p.requestBody != null) { + req = p.requestBody; + } + + r = rest.sendHttpRequest(req, p); + if (p.returnRequestPayload && req != null) { + ctx.setAttribute(pp + HTTP_REQ, req); + } + + String response = getResponse(ctx, p, pp, r); + if (response != null) { + try { + Map<String, String> resProp = serializeResponse( + p, uri, response, insIdCtx); + for (Map.Entry<String, String> pro : resProp.entrySet()) { + ctx.setAttribute(pro.getKey(), pro.getValue()); + } + } catch (SvcLogicException e) { + convertToNormalRes(ctx, p, pp, response); + } + } + } catch (SvcLogicException e) { + boolean shouldRetry = false; + if (e.getCause().getCause() instanceof SocketException) { + shouldRetry = true; + } + + log.error(REQ_ERR + e.getMessage(), e); + String prefix = parseParam(paramMap, RES_PRE, false, null); + setFailureResponseStatus(ctx, prefix, e.getMessage()); + } + + if (r != null && r.code >= 300) { + throw new SvcLogicException(valueOf(r.code) + + COLON + " " + r.message); + } + } + + private void convertToNormalRes(SvcLogicContext ctx , + YangParameters p, String pp, String body) + throws SvcLogicException { + if (p.convertResponse) { + Map<String, String> mm = null; + if (p.format == Format.XML) { + mm = XmlParser.convertToProperties(body, p.listNameList); + } else if (p.format == Format.JSON) { + mm = org.onap.ccsdk.sli.plugins.restapicall.JsonParser + .convertToProperties(body); + } + + if (mm != null) { + for (Map.Entry<String, String> entry : mm.entrySet()) { + ctx.setAttribute(pp + entry.getKey(), + entry.getValue()); + } + } + } + } + + /** + * Serializes the request message to JSON or XML from the properties. + * + * @param properties properties + * @param params YANG parameters + * @param uri URI + * @param insIdCtx instance identifier context + * @return JSON or XML message to be sent + * @throws SvcLogicException when serializing the request fails + */ + public String serializeRequest(Map<String, String> properties, + YangParameters params, String uri, + InstanceIdentifierContext insIdCtx) + throws SvcLogicException { + PropertiesNodeSerializer propSer = new MdsalPropertiesNodeSerializer( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + DataFormatSerializerContext serCtx = new DataFormatSerializerContext( + null, uri, null, propSer); + DataFormatSerializer ser = DfSerializerFactory.instance() + .getSerializer(serCtx, params); + //TODO: Handling of XML annotations + return ser.encode(properties, null); + } + + /** + * Serializes the response message from JSON or XML to the properties. + * + * @param params YANG parameters + * @param uri URI + * @param response response message + * @param insIdCtx instance identifier context + * @return response message as properties + * @throws SvcLogicException when serializing the response fails + */ + public Map<String, String> serializeResponse(YangParameters params, + String uri, String response, + InstanceIdentifierContext insIdCtx) + throws SvcLogicException { + PropertiesNodeSerializer propSer = new MdsalPropertiesNodeSerializer( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + SerializerHelper helper = new MdsalSerializerHelper( + insIdCtx.getSchemaNode(), insIdCtx.getSchemaContext(), uri); + Listener listener = instance().getListener(helper, params); + DataFormatSerializerContext serCtx = new DataFormatSerializerContext( + listener, uri, null, propSer); + DataFormatSerializer ser = DfSerializerFactory.instance() + .getSerializer(serCtx, params); + return ser.decode(response); + } + + /** + * Returns instance identifier context for a uri using the schema context. + * + * @param params YANG parameters + * @param uri URI + * @return instance identifier context + * @throws SvcLogicException when getting schema context fails + */ + private InstanceIdentifierContext<?> getInsIdCtx(YangParameters params, + String uri) + throws SvcLogicException { + SchemaContext context = getSchemaContext(params); + return ParserIdentifier.toInstanceIdentifier(uri, context, null); + } + + /** + * Returns the global schema context or schema context of particular YANG + * files present in a directory path. + * + * @param params YANG parameters + * @return schema context + * @throws SvcLogicException when schema context fetching fails + */ + private SchemaContext getSchemaContext(YangParameters params) + throws SvcLogicException { + if (params.dirPath != null) { + return getSchemaCtxFromDir(params.dirPath); + } + BundleContext bc = getBundle(SchemaContext.class).getBundleContext(); + SchemaContext schemaContext = null; + if (bc != null) { + ServiceReference reference = bc.getServiceReference( + SchemaContext.class); + if (reference != null) { + schemaContext = (SchemaContext) bc.getService(reference); + } + } + return schemaContext; + } + + /** + * Returns the response message body of a http response message. + * + * @param ctx svc logic context + * @param params parameters + * @param pre prefix to be appended + * @param res http response + * @return response message body + */ + public String getResponse(SvcLogicContext ctx, YangParameters params, + String pre, HttpResponse res) { + ctx.setAttribute(pre + RES_CODE, valueOf(res.code)); + ctx.setAttribute(pre + RES_MSG, res.message); + + if (params.dumpHeaders && res.headers != null) { + for (Map.Entry<String, List<String>> a : res.headers.entrySet()) { + ctx.setAttribute(pre + HEADER + a.getKey(), + join(a.getValue(), COMMA)); + } + } + + if (res.body != null && res.body.trim().length() > 0) { + ctx.setAttribute(pre + HTTP_RES, res.body); + return res.body; + } + return null; + } + + /** + * Sets the failure response status in the context memory. + * + * @param ctx service logic context + * @param prefix prefix to be added + * @param errMsg error message + */ + private void setFailureResponseStatus(SvcLogicContext ctx, String prefix, + String errMsg) { + HttpResponse res = new HttpResponse(); + res.code = 500; + res.message = errMsg; + ctx.setAttribute(prefix + RES_CODE, valueOf(res.code)); + ctx.setAttribute(prefix + RES_MSG, res.message); + } + + /** + * Updates request message for JSON and XML data format, when the HTTP + * method points it as PUT or PATCH. + * + * @param req current request message + * @param p YANG parameters + * @param insIdCtx instance identifier context + * @return update request message + * @throws SvcLogicException when the data format type is wrong + */ + public String updateReq(String req, YangParameters p, + InstanceIdentifierContext<?> insIdCtx) + throws SvcLogicException { + + SchemaNode schemaNode = insIdCtx.getSchemaNode(); + Namespace modNs = getModuleNamespace(schemaNode.getQName(), + insIdCtx.getSchemaContext()); + String nodeName = schemaNode.getQName().getLocalName(); + + switch (p.format) { + case JSON: + return getUpdatedJsonReq(req, nodeName, modNs.moduleName()); + + case XML: + return getXmlReqForPutOp(req, nodeName, modNs.moduleNs()); + + default: + throw new SvcLogicException(format(FORMAT_ERR, p.format)); + } + } + + /** + * Returns the updated JSON request message, when the HTTP method + * points to PUT or PATCH. + * + * @param req current JSON request message + * @param nodeName root node name + * @param modName module name of the root node + * @return update JSON request message + */ + private String getUpdatedJsonReq(String req, String nodeName, + String modName) { + Writer writer = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(repeat(" ", 4)); + + JsonParser jsonParser = new JsonParser(); + JsonObject oldJson = (JsonObject)jsonParser.parse(req); + oldJson = remChildModName(oldJson, modName); + JsonObject newJson = new JsonObject(); + newJson.add(modName + COLON + nodeName, oldJson.deepCopy()); + + Gson gson= new Gson(); + gson.toJson(newJson, jsonWriter); + return writer.toString(); + } + + /** + * Removes module name from all the updated first level child node, if it + * is same as the root node added. + * + * @param oldJson JSON object for old request + * @param modName module name of root node + * @return JSON object for old request with updated child module name + */ + private JsonObject remChildModName(JsonObject oldJson, String modName) { + Iterator<Map.Entry<String, JsonElement>> it = oldJson.entrySet().iterator(); + Map<String, JsonElement> m = new HashMap<>(); + while (it.hasNext()) { + Map.Entry<String, JsonElement> jNode = it.next(); + if (jNode.getKey().contains(COLON)) { + String[] modArr = jNode.getKey().split(COLON); + if (modArr[0].equals(modName)) { + it.remove(); + m.put(modArr[1], jNode.getValue()); + } + } + } + if (!m.isEmpty()) { + for (Map.Entry<String, JsonElement> element : m.entrySet()) { + oldJson.add(element.getKey(), element.getValue()); + } + } + return oldJson; + } + + /** + * Returns the updated XML request message, when the HTTP method points + * to PUT or PATCH. + * + * @param req current JSON request message + * @param nodeName root node name + * @param modNs module namespace of the root node + * @return update JSON request message + * @throws SvcLogicException when XML parsing fails + */ + private String getXmlReqForPutOp(String req, String nodeName, + URI modNs) throws SvcLogicException { + req = getUpdatedXmlReq(req, nodeName, modNs.toString()); + Document oldDoc; + try { + oldDoc = DocumentHelper.parseText(req); + } catch (DocumentException e) { + throw new SvcLogicException(XML_TREE_ERR, e); + } + Writer writer = getXmlWriter( + UTF_HEADER + oldDoc.getRootElement().asXML(), "4"); + return writer.toString(); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java new file mode 100644 index 000000000..1309102b3 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfapicall/RestconfApiUtils.java @@ -0,0 +1,265 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfapicall; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.HttpMethod; +import org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.YangParameters; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; + +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.getParameters; +import static org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode.parseParam; +import static org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode.DEFAULT_MODE; +import static org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource.forFile; +import static org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors.defaultReactor; +import static org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangStatementStreamSource.create; + +/** + * Utilities for restconf api call node. + */ +public final class RestconfApiUtils { + + static final String RES_CODE = "response-code"; + + static final String HTTP_REQ ="httpRequest"; + + static final String RES_PRE = "responsePrefix"; + + static final String RES_MSG = "response-message"; + + static final String HEADER = "header."; + + static final String COMMA = ","; + + static final String COLON = ":"; + + static final String HTTP_RES = "httpResponse"; + + static final String REST_API_URL = "restapiUrl"; + + static final String UPDATED_URL = "URL was set to"; + + static final String COMM_FAIL = "Failed to communicate with host %s." + + "Request will be re-attempted using the host %s."; + + static final String RETRY_COUNT = "This is retry attempt %d out of %d"; + + static final String RETRY_FAIL = "Retry attempt has failed. No further " + + "retry shall be attempted, calling setFailureResponseStatus"; + + static final String NO_MORE_RETRY = "Could not attempt retry"; + + static final String MAX_RETRY_ERR = "Maximum retries reached, calling " + + "setFailureResponseStatus"; + + static final String ATTEMPTS_MSG = "%d attempts were made out of %d " + + "maximum retries"; + + static final String REQ_ERR = "Error sending the request: "; + + private static final String SLASH = "/"; + + private static final String DIR_PATH = "dirPath"; + + private static final String URL_SYNTAX = "The following URL cannot be " + + "parsed into URI : "; + + private static final String YANG = ".yang"; + + private static final String YANG_FILE_ERR = "Unable to parse the YANG " + + "file provided"; + + //No instantiation. + private RestconfApiUtils() { + } + + /** + * Returns the YANG parameters after parsing it from the map. + * + * @param paramMap parameters map + * @return YANG parameters + * @throws SvcLogicException when parsing of parameters map fail + */ + static YangParameters getYangParameters(Map<String, String> paramMap) + throws SvcLogicException { + YangParameters param = (YangParameters) getParameters( + paramMap, new YangParameters()); + param.dirPath = parseParam(paramMap, DIR_PATH, false, null); + return param; + } + + /** + * Parses the restconf URL and gives the YANG path from it, which can be + * used to get schema node. If it is a PUT operation, then a node must be + * reduced from the url to make it always point to the parent. + * + * @param url restconf URL + * @param method HTTP operation + * @return YANG path pointing to parent + * @throws SvcLogicException when parsing the URL fails + */ + public static String parseUrl(String url, HttpMethod method) + throws SvcLogicException { + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + throw new SvcLogicException(URL_SYNTAX + url, e); + } + + String path = uri.getPath(); + path = getParsedPath(path); + return path; + } + + /** + * Returns the path which contains only the schema nodes. + * + * @param path path + * @return path representing schema + */ + private static String getParsedPath(String path) { + String firstHalf; + String secondHalf; + if (path.contains(COLON)) { + String[] p = path.split(COLON); + if (p[0].contains(SLASH)) { + int slash = p[0].lastIndexOf(SLASH); + firstHalf = p[0].substring(slash + 1); + } else { + firstHalf = p[0]; + } + secondHalf = path.substring(p[0].length() + 1); + return firstHalf + COLON + secondHalf; + } else if (path.contains(SLASH)) { + String[] p = path.split(SLASH); + if (p.length > 4) { + String actual = p[3] + COLON + p[4]; + if (p.length > 5) { + secondHalf = path.substring( + p[1].length() + p[2].length() + actual.length() + 3); + path = actual + secondHalf; + } else { + path = actual; + } + } + } + return path; + } + + /** + * Returns the schema context of the YANG files present in a directory. + * + * @param di directory path + * @return YANG schema context + * @throws SvcLogicException when YANG file reading fails + */ + static SchemaContext getSchemaCtxFromDir(String di) + throws SvcLogicException { + Path d = Paths.get(di); + File dir = d.toFile(); + List<File> yangFiles = new LinkedList<>(); + getYangFiles(dir, yangFiles); + final Collection<YangStatementStreamSource> sources = + new ArrayList<>(yangFiles.size()); + for (File file : yangFiles) { + try { + sources.add(create(forFile(file))); + } catch (IOException | YangSyntaxErrorException e) { + throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e); + } + } + + final CrossSourceStatementReactor.BuildAction reactor = defaultReactor() + .newBuild(DEFAULT_MODE).addSources(sources); + try { + return reactor.buildEffective(); + } catch (ReactorException e) { + throw new SvcLogicException(YANG_FILE_ERR + e.getMessage(), e); + } + } + + /** + * Returns all the YANG files present in a directory recursively. + * + * @param dir path of the directory + * @param yangFiles list of YANG files + */ + private static void getYangFiles(File dir, List<File> yangFiles) { + if (dir.exists()) { + File[] files = dir.listFiles(); + if (files != null) { + processFiles(files, yangFiles); + } + } + } + + /** + * Processes all the obtained files by isolating all the YANG files from + * all the directory of the given path recursively. + * + * @param files files in the given path + * @param yangFiles YANG files list + */ + private static void processFiles(File[] files, List<File> yangFiles) { + for (File file : files) { + if (file.isFile() && file.getName().endsWith(YANG)) { + yangFiles.add(file); + } else if (file.isDirectory()) { + getYangFiles(file, yangFiles); + } + } + } + + /** + * Returns the updated XML request message by adding root node to it. + * + * @param req XML request + * @param nodeName root node name + * @param modNs module namespace of the root node + * @return updated XML request message + */ + static String getUpdatedXmlReq(String req, String nodeName, String modNs) { + String rootNode = "\n<" + nodeName + " xmlns=\"" + modNs + + "\">\n"; + req = req.replaceFirst("\n", rootNode); + req = req + "</" + nodeName + ">"; + return req.replaceAll(">\\s+<", "><"); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java new file mode 100644 index 000000000..3213cae15 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/AdditionalHeaderWebTarget.java @@ -0,0 +1,166 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import javax.ws.rs.client.Invocation; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.util.Map; + +/** + * Adds additional headers for SSE request. + */ +class AdditionalHeaderWebTarget implements WebTarget { + private WebTarget base; + private String token; + + private String accessToken = "X-ACCESS-TOKEN"; + + public AdditionalHeaderWebTarget(WebTarget target, String token) { + base = target; + this.token = token; + } + + @Override + public Invocation.Builder request() { + return base.request().header(accessToken, token); + } + + @Override + public Invocation.Builder request(String... acceptedResponseTypes) { + return base.request().header(accessToken, token); + } + + @Override + public Invocation.Builder request(MediaType... acceptedResponseTypes) { + return base.request().header(accessToken, token); + } + + @Override + public Configuration getConfiguration() { + return base.getConfiguration(); + } + + @Override + public URI getUri() { + return base.getUri(); + } + + @Override + public UriBuilder getUriBuilder() { + return base.getUriBuilder(); + } + + @Override + public WebTarget path(String path) { + return base.path(path); + } + + @Override + public WebTarget resolveTemplate(String name, Object value) { + return base.resolveTemplate(name, value); + } + + @Override + public WebTarget resolveTemplate(String name, Object value, boolean encodeSlashInPath) { + return base.resolveTemplate(name, value, encodeSlashInPath); + } + + @Override + public WebTarget resolveTemplateFromEncoded(String name, Object value) { + return base.resolveTemplateFromEncoded(name, value); + } + + @Override + public WebTarget resolveTemplates(Map<String, Object> templateValues) { + return base.resolveTemplates(templateValues); + } + + @Override + public WebTarget resolveTemplates(Map<String, Object> templateValues, boolean encodeSlashInPath) { + return base.resolveTemplates(templateValues, encodeSlashInPath); + } + + @Override + public WebTarget resolveTemplatesFromEncoded(Map<String, Object> templateValues) { + return base.resolveTemplatesFromEncoded(templateValues); + } + + @Override + public WebTarget matrixParam(String name, Object... values) { + return base.matrixParam(name, values); + } + + @Override + public WebTarget queryParam(String name, Object... values) { + return base.queryParam(name, values); + } + + @Override + public WebTarget property(String name, Object value) { + return base.property(name, value); + } + + @Override + public WebTarget register(Class<?> componentClass) { + return base.register(componentClass); + } + + @Override + public WebTarget register(Class<?> componentClass, int priority) { + return base.register(componentClass, priority); + } + + @Override + public WebTarget register(Class<?> componentClass, Class<?>... contracts) { + return base.register(componentClass, contracts); + } + + @Override + public WebTarget register(Class<?> componentClass, Map<Class<?>, Integer> contracts) { + return base.register(componentClass, contracts); + } + + @Override + public WebTarget register(Object component) { + return base.register(component); + } + + @Override + public WebTarget register(Object component, int priority) { + return base.register(component, priority); + } + + @Override + public WebTarget register(Object component, Class<?>... contracts) { + return base.register(component, contracts); + } + + @Override + public WebTarget register(Object component, Map<Class<?>, Integer> contracts) { + return base.register(component, contracts); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventHandler.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventHandler.java new file mode 100644 index 000000000..a76554d5a --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventHandler.java @@ -0,0 +1,46 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.glassfish.jersey.media.sse.InboundEvent; +import org.glassfish.jersey.media.sse.EventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Listener that can be registered to listen for notifications. + */ +class EventHandler implements EventListener { + private static final Logger log = LoggerFactory.getLogger(EventListener.class); + private RestconfDiscoveryNode node; + + public EventHandler(RestconfDiscoveryNode node) { + this.node = node; + } + + @Override + public void onEvent(InboundEvent event) { + String payload = event.readData(); + if (!node.eventQueue().offer(payload)) { + log.error("Unable to process event {} as processing queue is full", payload); + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventProcessor.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventProcessor.java new file mode 100644 index 000000000..4f28072d3 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/EventProcessor.java @@ -0,0 +1,76 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; + +import java.util.Map; + +import static org.onap.ccsdk.sli.plugins.restapicall.JsonParser.convertToProperties; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Processes the events from event queue and executes callback DG. + */ +class EventProcessor implements Runnable { + + private static final Logger log = getLogger(EventProcessor.class); + private RestconfDiscoveryNode node; + + private static final String EVENT_SUBSCRIPTION_ID = "notification." + + "push-change-update.subscription-id"; + + public EventProcessor(RestconfDiscoveryNode node) { + this.node = node; + } + + @Override + public void run() { + while(true) { + try { + String payload = node.eventQueue().take(); + Map<String, String> param = convertToProperties(payload); + String id = param.get(EVENT_SUBSCRIPTION_ID); + SubscriptionInfo info = node.subscriptionInfoMap().get(id); + if (info != null) { + SvcLogicContext ctx = setContext(param); + SvcLogicGraphInfo callbackDG = info.callBackDG(); + callbackDG.executeGraph(ctx); + } + } catch (InterruptedException | SvcLogicException e) { + log.error("Interrupted!", e); + Thread.currentThread().interrupt(); + } + } + } + + private SvcLogicContext setContext(Map<String, String> param) { + SvcLogicContext ctx = new SvcLogicContext(); + for (Map.Entry<String, String> entry : param.entrySet()) { + ctx.setAttribute(entry.getKey(), entry.getValue()); + } + return ctx; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java new file mode 100644 index 000000000..cf69d7a3c --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/RestconfDiscoveryNode.java @@ -0,0 +1,310 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.glassfish.jersey.media.sse.EventSource; +import org.glassfish.jersey.media.sse.SseFeature; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.Parameters; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; +import org.slf4j.Logger; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Representation of a plugin to subscribe for notification and then + * to handle the received notifications. + */ +public class RestconfDiscoveryNode implements SvcLogicDiscoveryPlugin { + + private static final Logger log = getLogger(RestconfDiscoveryNode.class); + + private ExecutorService executor = Executors.newCachedThreadPool(); + private Map<String, PersistentConnection> runnableInfo = new ConcurrentHashMap<>(); + private RestconfApiCallNode restconfApiCallNode; + + private volatile Map<String, SubscriptionInfo> subscriptionInfoMap = new ConcurrentHashMap<>(); + private volatile LinkedBlockingQueue<String> eventQueue = new LinkedBlockingQueue<>(); + + private static final String SUBSCRIBER_ID = "subscriberId"; + private static final String RESPONSE_CODE = "response-code"; + private static final String RESPONSE_PREFIX = "responsePrefix"; + private static final String OUTPUT_IDENTIFIER = "ietf-subscribed-notif" + + "ications:establish-subscription.output.identifier"; + private static final String RESPONSE_CODE_200 = "200"; + private static final String SSE_URL = "sseConnectURL"; + + /** + * Creates an instance of RestconfDiscoveryNode and starts processing of + * event. + * + * @param r restconf api call node + */ + public RestconfDiscoveryNode(RestconfApiCallNode r) { + this.restconfApiCallNode = r; + ExecutorService e = Executors.newFixedThreadPool(20); + EventProcessor p = new EventProcessor(this); + for (int i = 0; i < 20; ++i) { + e.execute(p); + } + } + + @Override + public void establishSubscription(Map<String, String> paramMap, + SvcLogicContext ctx) throws SvcLogicException { + String subscriberId = paramMap.get(SUBSCRIBER_ID); + if (subscriberId == null) { + throw new SvcLogicException("Subscriber Id is null"); + } + + restconfApiCallNode.sendRequest(paramMap, ctx); + + if (getResponseCode(paramMap.get(RESPONSE_PREFIX), ctx).equals(RESPONSE_CODE_200)) { + // TODO: save subscription id and subscriber in MYSQL + + establishPersistentConnection(paramMap, ctx, subscriberId); + } else { + log.info("Failed to subscribe {}", subscriberId); + throw new SvcLogicException(ctx.getAttribute(RESPONSE_CODE)); + } + } + + @Override + public void modifySubscription(Map<String, String> paramMap, SvcLogicContext ctx) { + // TODO: to be implemented + } + + @Override + public void deleteSubscription(Map<String, String> paramMap, SvcLogicContext ctx) { + String id = getSubscriptionId(paramMap.get(SUBSCRIBER_ID)); + if (id != null) { + PersistentConnection conn = runnableInfo.get(id); + conn.terminate(); + runnableInfo.remove(id); + subscriptionInfoMap.remove(id); + } + } + + class PersistentConnection implements Runnable { + private String url; + private volatile boolean running = true; + private Map<String, String> paramMap; + + PersistentConnection(String url, Map<String, String> paramMap) { + this.url = url; + this.paramMap = paramMap; + } + + private void terminate() { + running = false; + } + + @Override + public void run() { + Parameters p; + WebTarget target = null; + try { + RestapiCallNode restapi = restconfApiCallNode.getRestapiCallNode(); + p = RestapiCallNode.getParameters(paramMap, new Parameters()); + Client client = ignoreSslClient().register(SseFeature.class); + target = restapi.addAuthType(client, p).target(url); + } catch (SvcLogicException e) { + log.error("Exception occured!", e); + Thread.currentThread().interrupt(); + } + + target = addToken(target, paramMap.get("customHttpHeaders")); + EventSource eventSource = EventSource.target(target).build(); + eventSource.register(new EventHandler(RestconfDiscoveryNode.this)); + eventSource.open(); + log.info("Connected to SSE source"); + while (running) { + try { + log.info("SSE state " + eventSource.isOpen()); + Thread.sleep(5000); + } catch (InterruptedException e) { + log.error("Interrupted!", e); + Thread.currentThread().interrupt(); + } + } + eventSource.close(); + log.info("Closed connection to SSE source"); + } + + private Client ignoreSslClient() { + SSLContext sslcontext = null; + + try { + sslcontext = SSLContext.getInstance("TLS"); + sslcontext.init(null, new TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + } }, new java.security.SecureRandom()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IllegalStateException(e); + } + + return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((s1, s2) -> true).build(); + } + } + + protected String getTokenId(String customHttpHeaders) { + if (customHttpHeaders.contains("=")) { + String[] s = customHttpHeaders.split("="); + return s[1]; + } + return customHttpHeaders; + } + + protected WebTarget addToken(WebTarget target, String customHttpHeaders) { + if (customHttpHeaders == null) { + return target; + } + + return new AdditionalHeaderWebTarget( + target, getTokenId(customHttpHeaders)); + } + + /** + * Establishes a persistent between the client and server. + * + * @param paramMap input paramter map + * @param ctx service logic context + * @param subscriberId subscriber identifier + */ + void establishPersistentConnection(Map<String, String> paramMap, SvcLogicContext ctx, + String subscriberId) { + String id = getOutputIdentifier(paramMap.get(RESPONSE_PREFIX), ctx); + SvcLogicGraphInfo callbackDG = new SvcLogicGraphInfo(paramMap.get("module"), + paramMap.get("rpc"), + paramMap.get("version"), + paramMap.get("mode")); + SubscriptionInfo info = new SubscriptionInfo(); + info.callBackDG(callbackDG); + info.subscriptionId(id); + info.subscriberId(subscriberId); + subscriptionInfoMap.put(id, info); + + String url = paramMap.get(SSE_URL); + PersistentConnection connection = new PersistentConnection(url, paramMap); + runnableInfo.put(id, connection); + executor.execute(connection); + } + + /** + * Returns response code. + * + * @param prefix prefix given in input parameter + * @param ctx service logic context + * @return response code + */ + String getResponseCode(String prefix, SvcLogicContext ctx) { + return ctx.getAttribute(getPrefix(prefix) + RESPONSE_CODE); + } + + /** + * Returns subscription id from event. + * + * @param prefix prefix given in input parameter + * @param ctx service logic context + * @return subscription id from event + */ + String getOutputIdentifier(String prefix, SvcLogicContext ctx) { + return ctx.getAttribute(getPrefix(prefix) + OUTPUT_IDENTIFIER); + } + + private String getPrefix(String prefix) { + return prefix != null ? prefix + "." : ""; + } + + private String getSubscriptionId(String subscriberId) { + for (Map.Entry<String,SubscriptionInfo> entry + : subscriptionInfoMap.entrySet()) { + if (entry.getValue().subscriberId() + .equals(subscriberId)) { + return entry.getKey(); + } + } + return null; + } + + /** + * Returns restconfApiCallNode. + * + * @return restconfApiCallNode + */ + protected RestconfApiCallNode restconfapiCallNode() { + return restconfApiCallNode; + } + + /** + * Sets restconfApiCallNode. + * + * @param node restconfApiCallNode + */ + void restconfapiCallNode(RestconfApiCallNode node) { + restconfApiCallNode = node; + } + + Map<String, SubscriptionInfo> subscriptionInfoMap() { + return subscriptionInfoMap; + } + + void subscriptionInfoMap(Map<String, SubscriptionInfo> subscriptionInfoMap) { + this.subscriptionInfoMap = subscriptionInfoMap; + } + + LinkedBlockingQueue<String> eventQueue() { + return eventQueue; + } + + void eventQueue(LinkedBlockingQueue<String> eventQueue) { + this.eventQueue = eventQueue; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SubscriptionInfo.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SubscriptionInfo.java new file mode 100644 index 000000000..4ed3660ca --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SubscriptionInfo.java @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +/** + * Holder to store information of subscription. + */ +public class SubscriptionInfo { + private String subscriptionId; + private String subscriberId; + private SvcLogicGraphInfo callbackDG; + private String yangFilePath; + private String filterUrl; + + /** + * Returns callback DG. + * + * @return callback DG + */ + public SvcLogicGraphInfo callBackDG() { + return callbackDG; + } + + /** + * Sets callback DG. + * + * @param callbackDg callback DG + */ + public void callBackDG(SvcLogicGraphInfo callbackDg) { + this.callbackDG = callbackDg; + } + + /** + * Returns YANG file path. + * + * @return YANG file path + */ + public String yangFilePath() { + return yangFilePath; + } + + /** + * Sets YANG file path. + * + * @param yangFilePath yang file path + */ + public void yangFilePath(String yangFilePath) { + this.yangFilePath = yangFilePath; + } + + /** + * Returns filter URL. + * + * @return filter URL + */ + public String filterUrl() { + return filterUrl; + } + + /** + * Sets filter URL. + * + * @param filterUrl filter URL + */ + public void filterUrl(String filterUrl) { + this.filterUrl = filterUrl; + } + + /** + * Returns subscription Id. + * + * @return subscription Id + */ + public String subscriptionId() { + return subscriptionId; + } + + /** + * Sets subscription id. + * + * @param subscriptionId subscription id + */ + public void subscriptionId(String subscriptionId) { + this.subscriptionId = subscriptionId; + } + + /** + * Returns subscription Id. + * + * @return subscription Id + */ + public String subscriberId() { + return subscriberId; + } + + /** + * Sets subscriber id. + * + * @param subscriberId subscriber id + */ + public void subscriberId(String subscriberId) { + this.subscriberId = subscriberId; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicDiscoveryPlugin.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicDiscoveryPlugin.java new file mode 100644 index 000000000..dfe8cd5b7 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicDiscoveryPlugin.java @@ -0,0 +1,110 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import java.util.Map; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; + +/** + * Abstraction of a plugin to enable discovery from DG. + */ +public interface SvcLogicDiscoveryPlugin extends SvcLogicJavaPlugin { + + /** + * Allows directed graphs to establish a discovery subscription for a given subscriber. + * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>templateDirName</td><td>Optional</td><td>full path to YANG directory that can be used to build a request</td><td>/sdncopt/bvc/resconfapi/test</td></tr> + * <tr><td>establishSubscriptionURL</td><td>Mandatory</td><td>url to establish connection with server</td><td>https://127.0.0.1:8181/restconf/operations/ietf-subscribed-notifications:establish-subscription</td></tr> + * <tr><td>sseConnectURL</td><td>Mandatory</td><td>url to setup SSE connection with server</td><td>https://127.0.0.1:8181/restconf/streams/yang-push-json</td></tr> + * <tr><td>callbackDG</td><td>Mandatory</td><td>callback DG to process the received notification</td><td>Resource-Discovery:handleSOTNTopology</td></tr> + * <tr><td>filterURL</td><td>Optional</td><td>url which needs to be subscribed, if null subscribe to all</td><td>http://example.com/sample-data/1.0</td></tr> + * <tr><td>subscriptionType</td><td>Optional</td><td>type of subscription, periodic or onDataChange</td><td>onDataChange</td></tr> + * <tr><td>updateFrequency</td><td>Optional</td><td>update frequency in milli seconds when subscription type is periodic</td><td>1000</td></tr> + * <tr><td>restapiUser</td><td>Optional</td><td>user name to use for http basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>restapiPassword</td><td>Optional</td><td>unencrypted password to use for http basic authentication</td><td>plain_password</td></tr> + * <tr><td>contentType</td><td>Optional</td><td>http content type to set in the http header</td><td>usually application/json or application/xml</td></tr> + * <tr><td>format</td><td>Optional</td><td>should match request body format</td><td>json or xml</td></tr> + * <tr><td>responsePrefix</td><td>Optional</td><td>location the notification response will be written to in context memory</td><td>tmp.restconfdiscovery.result</td></tr> + * <tr><td>skipSending</td><td>Optional</td><td></td><td>true or false</td></tr> + * <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>customHttpHeaders</td><td>Optional</td><td>a list additional http headers to be passed in, follow the format in the example</td><td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td></tr> + * <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes http header content to context memory</td><td>true or false</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + * @see String#split(String, int) + */ + void establishSubscription(Map<String, String> paramMap, SvcLogicContext ctx) throws SvcLogicException; + + /** + * Allows directed graphs to modify a discovery subscription for a given subscriber. + * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>subscriberId</td><td>Mandatory</td><td>subscription subscriber's identifier</td><td>topologyId/1111</td></tr> + * <tr><td>templateDirName</td><td>Optional</td><td>full path to YANG directory that can be used to build a request</td><td>/sdncopt/bvc/resconfapi/test</td></tr> + * <tr><td>establishSubscriptionURL</td><td>Mandatory</td><td>url to establish connection with server</td><td>https://127.0.0.1:8181/restconf/operations/ietf-subscribed-notifications:establish-subscription</td></tr> + * <tr><td>sseConnectURL</td><td>Mandatory</td><td>url to setup SSE connection with server</td><td>https://127.0.0.1:8181/restconf/streams/yang-push-json</td></tr> + * <tr><td>callbackDG</td><td>Mandatory</td><td>callback DG to process the received notification</td><td>Resource-Discovery:handleSOTNTopology</td></tr> + * <tr><td>filterURL</td><td>Optional</td><td>url filter list which needs to be subscribed, if null subscribe to all</td><td>http://example.com/sample-data/1.0</td></tr> + * <tr><td>subscriptionType</td><td>Optional</td><td>type of subscription, periodic or onDataChange</td><td>onDataChange</td></tr> + * <tr><td>updateFrequency</td><td>Optional</td><td>update frequency in milli seconds when subscription type is periodic</td><td>1000</td></tr> + * <tr><td>restapiUser</td><td>Optional</td><td>user name to use for http basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>restapiPassword</td><td>Optional</td><td>unencrypted password to use for http basic authentication</td><td>plain_password</td></tr> + * <tr><td>contentType</td><td>Optional</td><td>http content type to set in the http header</td><td>usually application/json or application/xml</td></tr> + * <tr><td>format</td><td>Optional</td><td>should match request body format</td><td>json or xml</td></tr> + * <tr><td>responsePrefix</td><td>Optional</td><td>location the notification response will be written to in context memory</td><td>tmp.restconfdiscovery.result</td></tr> + * <tr><td>skipSending</td><td>Optional</td><td></td><td>true or false</td></tr> + * <tr><td>convertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>customHttpHeaders</td><td>Optional</td><td>a list additional http headers to be passed in, follow the format in the example</td><td>X-CSI-MessageId=messageId,headerFieldName=headerFieldValue</td></tr> + * <tr><td>dumpHeaders</td><td>Optional</td><td>when true writes http header content to context memory</td><td>true or false</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + * @since 11.0.2 + * @see String#split(String, int) + */ + void modifySubscription(Map<String, String> paramMap, SvcLogicContext ctx); + + /** + * Allows directed graphs to delete the discovery subscription for a given subscriber. + * @param paramMap HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>subscriberId</td><td>Mandatory</td><td>subscription subscriber's identifier</td><td>topologyId/1111</td></tr> + * </tbody> + * </table> + * @param ctx Reference to context memory + * @throws SvcLogicException + */ + void deleteSubscription(Map<String, String> paramMap, SvcLogicContext ctx); + +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicGraphInfo.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicGraphInfo.java new file mode 100644 index 000000000..1cc6261ab --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SvcLogicGraphInfo.java @@ -0,0 +1,178 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicGraph; +import org.onap.ccsdk.sli.core.sli.SvcLogicStore; +import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; + +/** + * Holder to store callback directed graph info. + */ +class SvcLogicGraphInfo { + private String module; + private String rpc; + private String mode; + private String version; + + /** + * Creates an instance of SvcLogicGraphInfo. + * + * @param module module name of callback DG + * @param rpc rpc name of callback DG + * @param mode mode of callback DG + * @param version version of callback DG + */ + public SvcLogicGraphInfo(String module, String rpc, String mode, String version) { + this.module = module; + this.rpc = rpc; + this.mode = mode; + this.version = version; + } + + public SvcLogicGraphInfo() {} + + /** + * Returns module name of callback DG. + * + * @return module name of callback DG + */ + public String module() { + return module; + } + + /** + * Sets module of callback DG. + * + * @param module module name of the DG + */ + public void module(String module) { + this.module = module; + } + + /** + * Returns rpc of callback DG. + * + * @return rpc of callback DG + */ + public String rpc() { + return rpc; + } + + /** + * Sets rpc of callback DG. + * + * @param rpc rpc attribute of the DG + */ + public void rpc(String rpc) { + this.rpc = rpc; + } + + /** + * Returns mode of callback DG. + * + * @return mode of callback DG + */ + public String mode() { + return mode; + } + + /** + * Sets mode of DG. + * + * @param mode mode of the DG + */ + public void mode(String mode) { + this.mode = mode; + } + + /** + * Returns version of callback DG. + * + * @return version of callback DG + */ + public String version() { + return version; + } + + /** + * Sets version of DG. + * + * @param version version of the DG + */ + public void version(String version) { + this.version = version; + } + + /** + * Executes call back DG. + * + * @param ctx service logic context + * @throws SvcLogicException service logic error + */ + public void executeGraph(SvcLogicContext ctx) throws SvcLogicException { + SvcLogicService service = findSvcLogicService(); + if (service == null) { + throw new SvcLogicException("\"Could not get SvcLogicService reference\""); + } + + SvcLogicStore store = service.getStore(); + if (store != null) { + SvcLogicGraph subGraph = store.fetch(module, rpc, mode, version); + if (subGraph != null) { + ctx.setAttribute("subGraph", subGraph.toString()); + service.execute(subGraph, ctx); + } else { + throw new SvcLogicException("Failed to call child [" + module + + "," + rpc + "," + version + + "," + mode + "] because" + + " the" + " graph could" + + " not be found"); + } + } else { + throw new SvcLogicException("\"Could not get SvcLogicStore reference\""); + } + } + + private static SvcLogicService findSvcLogicService() throws SvcLogicException { + Bundle bundle = FrameworkUtil.getBundle(SvcLogicService.class); + if (bundle == null) { + throw new SvcLogicException("Cannot find bundle reference for " + + SvcLogicService.NAME); + } + + BundleContext bctx = bundle.getBundleContext(); + ServiceReference<SvcLogicService> sref = bctx.getServiceReference( + SvcLogicService.class); + if (sref != null) { + return bctx.getService(sref); + } else { + throw new SvcLogicException("Cannot find service reference for " + + SvcLogicService.NAME); + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Annotation.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Annotation.java new file mode 100644 index 000000000..185f70ba0 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Annotation.java @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +/** + * Representation of an entity that represents annotated attribute. + */ +public class Annotation { + + /** + * Name of the annotation. + */ + private String name; + + /** + * Value of the annotation. + */ + private String value; + + /** + * Creates an instance of annotation. + * + * @param n annotation name + * @param v annotation value + */ + public Annotation(String n, String v) { + name = n; + value = v; + } + + /** + * Returns name of annotation. + * + * @return name of annotation + */ + public String name() { + return name; + } + + /** + * Returns value of annotation. + * + * @return value of annotation + */ + public String value() { + return value; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormat.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormat.java new file mode 100644 index 000000000..c692f1dd2 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormat.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +/** + * Representation of data format. + */ +public enum DataFormat { + + /** + * XML data format. + */ + XML, + + /** + * JSON data format. + */ + JSON +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializer.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializer.java new file mode 100644 index 000000000..cab64391a --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializer.java @@ -0,0 +1,94 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import java.util.List; +import java.util.Map; + +/** + * Abstraction of serializer to encode/decode context memory parameters + * to/from specified data format. + */ +public abstract class DataFormatSerializer { + + /** + * Data format type of the serializer. + */ + private DataFormat dataFormat; + + /** + * Data format serializer context. + */ + private DataFormatSerializerContext serializerContext; + + /** + * Creates an instance of data format serializer. + * + * @param d type of data format + * @param s data format serializer context + */ + protected DataFormatSerializer(DataFormat d, + DataFormatSerializerContext s) { + this.dataFormat = d; + this.serializerContext = s; + } + + /** + * Encodes context memory parameters to data format. + * + * @param param context memory parameter + * @param annotations annotations + * @return data format body + * @throws SvcLogicException when serialization fails + */ + public abstract String encode(Map<String, String> param, + Map<String, List<Annotation>> annotations) throws SvcLogicException; + + /** + * Decodes data format body to context memory parameters. + * + * @param dataFormatBody abstract node + * @return context memory parameters + * @throws SvcLogicException when serialization fails + */ + public abstract Map<String, String> decode(String dataFormatBody) + throws SvcLogicException; + + /** + * Returns data format serializer context. + * + * @return data format serializer context + */ + public DataFormatSerializerContext serializerContext() { + return serializerContext; + } + + /** + * Returns supported data format. + * + * @return supported data format + */ + public DataFormat dataFormat() { + return dataFormat; + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerContext.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerContext.java new file mode 100644 index 000000000..919e82af3 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerContext.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeSerializer; + +import java.util.Map; + +/** + * Abstraction of data format serializer context. + */ +public class DataFormatSerializerContext { + + /** + * Data format listener. + */ + private Listener listener; + + /** + * URI corresponding to the instance identifier. + */ + private String uri; + + /** + * Protocol annotation. + */ + private Map<String, String> protocolAnnotation; + + /** + * Properties node serializer. + */ + private PropertiesNodeSerializer propNodeSerializer; + + /** + * Creates an instance of data format serializer context. + * + * @param l data format listener + * @param u URI corresponding to instance identifier + * @param p protocol annotations + * @param s properties node serializer + */ + public DataFormatSerializerContext(Listener l, String u, + Map<String, String> p, + PropertiesNodeSerializer s) { + listener = l; + uri = u; + protocolAnnotation = p; + propNodeSerializer = s; + } + + /** + * Returns the data format listener. + * + * @return data format listener + */ + public Listener listener() { + return listener; + } + + /** + * Returns the URI. + * + * @return URI + */ + public String uri() { + return uri; + } + + /** + * Returns the protocol annotations. + * + * @return protocol annotations + */ + public Map<String, String> getProtocolAnnotation() { + return protocolAnnotation; + } + + /** + * Returns the properties node serializer. + * + * @return properties node serializer + */ + public PropertiesNodeSerializer getPropNodeSerializer() { + return propNodeSerializer; + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonListener.java new file mode 100644 index 000000000..45317522a --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonListener.java @@ -0,0 +1,145 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.fasterxml.jackson.databind.JsonNode; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.NODE_TYPE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_NODE; + + +/** + * Representation of default implementation of JSON listener. + */ +public class DefaultJsonListener implements JsonListener { + + /** + * Serializer helper to convert to properties node. + */ + private SerializerHelper serializerHelper; + + /** + * Name of the current JSON node. + */ + private String name; + + /** + * Module name of the current JSON node. + */ + private String modName; + + /** + * Value of the current JSON node. + */ + private String value; + + /** + * Value namespace of the current JSON node. + */ + private String valueNs; + + /** + * Creates an instance of default json listener with its serializer helper. + * + * @param serializerHelper serializer helper + */ + public DefaultJsonListener(SerializerHelper serializerHelper) { + this.serializerHelper = serializerHelper; + } + + @Override + public void enterJsonNode(String nodeName, JsonNode node, + NodeType nodeType) throws SvcLogicException { + getNodeName(nodeName, false); + + switch (nodeType) { + case SINGLE_INSTANCE_LEAF_NODE: + getNodeName(node.asText(), true); + serializerHelper.addNode(name, modName, value, valueNs, + SINGLE_INSTANCE_LEAF_NODE); + break; + + case MULTI_INSTANCE_LEAF_NODE: + getNodeName(node.asText(), true); + serializerHelper.addNode(name, modName, value, valueNs, + MULTI_INSTANCE_LEAF_NODE); + break; + + case SINGLE_INSTANCE_NODE: + serializerHelper.addNode(name, modName, null, null, + SINGLE_INSTANCE_NODE); + break; + + case MULTI_INSTANCE_NODE: + serializerHelper.addNode(name, modName, null, null, + MULTI_INSTANCE_NODE); + break; + + default: + throw new SvcLogicException(format(NODE_TYPE_ERR, + nodeType.toString())); + } + } + + @Override + public void exitJsonNode(JsonNode node) throws SvcLogicException { + serializerHelper.exitNode(); + } + + @Override + public SerializerHelper serializerHelper() { + return serializerHelper; + } + + /** + * Parses the abstract JSON name and fills the node name and node + * namespace or value and value namespace of the current JSON node . + * + * @param abstractName full name value + * @param isVal if it is for value parsing + */ + private void getNodeName(String abstractName, boolean isVal) { + String[] val = abstractName.split(":"); + if (val.length == 2) { + if (isVal) { + valueNs = val[0]; + value = val[1]; + } else { + modName = val[0]; + name = val[1]; + } + } else { + if (isVal) { + value = val[0]; + valueNs = null; + } else { + name = val[0]; + modName = null; + } + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonWalker.java new file mode 100644 index 000000000..47cb8b292 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultJsonWalker.java @@ -0,0 +1,141 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeType; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType; + +import java.util.Iterator; +import java.util.Map; + +import static com.fasterxml.jackson.databind.node.JsonNodeType.NUMBER; +import static com.fasterxml.jackson.databind.node.JsonNodeType.STRING; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_NODE; + +/** + * Implementation of JSON walker to walk through the nodes and process it. + */ +public class DefaultJsonWalker implements JsonWalker { + + @Override + public void walk(JsonListener listener, JsonNode jsonNode) throws + SvcLogicException { + Iterator<Map.Entry<String, JsonNode>> children = jsonNode.fields(); + while (children.hasNext()) { + Map.Entry<String, JsonNode> child = children.next(); + JsonNode value = child.getValue(); + String key = child.getKey(); + if (value.isArray()) { + processMultiNodes(key, value, listener); + } else { + processSingleNode(key, value, listener); + } + } + } + + /** + * Processes single instance node or leaf, by adding the node to from + * JSON and walking through all its children recursively. + * + * @param key JSON name + * @param value JSON node + * @param listener JSON listener + * @throws SvcLogicException when processing the node fails + */ + private void processSingleNode(String key, JsonNode value, + JsonListener listener) + throws SvcLogicException { + NodeType nodeType; + if (!value.isContainerNode()) { + nodeType = SINGLE_INSTANCE_LEAF_NODE; + } else { + nodeType = SINGLE_INSTANCE_NODE; + } + processNode(key, value, nodeType, listener); + } + + /** + * Processes multi instance node or leaf, by adding the node to from JSON + * and walking through all its instance recursively. + * + * @param key JSON name + * @param value JSON node + * @param listener JSON listener + * @throws SvcLogicException when processing a single instance fails + */ + private void processMultiNodes(String key, JsonNode value, + JsonListener listener) + throws SvcLogicException { + NodeType nodeType; + Iterator<JsonNode> multiNodes = value.elements(); + while (multiNodes.hasNext()) { + if (isLeafListNode((ArrayNode) value)) { + nodeType = MULTI_INSTANCE_LEAF_NODE; + } else { + nodeType = MULTI_INSTANCE_NODE; + } + JsonNode multiNode = multiNodes.next(); + processNode(key, multiNode, nodeType, listener); + } + } + + /** + * Processes each node by first entering the JSON node through JSON + * listener, second a call back to walking the rest of the tree of the + * node and finally exiting the node. + * + * @param key JSON name + * @param node JSON node + * @param nodeType JSON node type + * @param listener JSON listener + * @throws SvcLogicException when entering a JSON node fails + */ + private void processNode(String key, JsonNode node, NodeType nodeType, + JsonListener listener) throws SvcLogicException { + listener.enterJsonNode(key, node, nodeType); + walk(listener, node); + listener.exitJsonNode(node); + } + + /** + * Returns true if the node corresponds to a leaf-list node; false + * otherwise. + * + * @param node JSON node + * @return true if node corresponds to leaf-list node; false otherwise + */ + private boolean isLeafListNode(ArrayNode node) { + Iterator<JsonNode> children = node.elements(); + while (children.hasNext()) { + JsonNodeType type = children.next().getNodeType(); + if (type != STRING && type != NUMBER) { + return false; + } + } + return true; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlListener.java new file mode 100644 index 000000000..03abf44fd --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlListener.java @@ -0,0 +1,120 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Element; +import org.dom4j.Namespace; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import java.util.List; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.NODE_TYPE_ERR; + +/** + * Representation of default implementation of XML listener. + */ +public class DefaultXmlListener implements XmlListener { + + /** + * Serializer helper to convert to properties node. + */ + private SerializerHelper serializerHelper; + + /** + * Creates an instance of default XML listener with its serializer helper. + * + * @param serializerHelper serializer helper + */ + public DefaultXmlListener(SerializerHelper serializerHelper) { + this.serializerHelper = serializerHelper; + } + + @Override + public void enterXmlElement(Element element, XmlNodeType nodeType) + throws SvcLogicException { + switch (nodeType) { + case TEXT_NODE: + serializerHelper.addNode(element.getName(), + element.getNamespace().getURI(), + element.getText(), null, null); + break; + + case OBJECT_NODE: + List cont = element.content(); + if (cont != null && cont.size() == 2 && + isValueNsForLeaf(cont, element)) { + return; + } + serializerHelper.addNode(element.getName(), + element.getNamespace().getURI(), + null, null, null); + break; + + default: + throw new SvcLogicException(format(NODE_TYPE_ERR, + nodeType.toString())); + } + } + + /** + * Returns true if element has value namespace and adds the node to + * property tree; false otherwise. + * + * @param cont content of the element + * @param element element + * @return true if element has value namespace; false otherwise + * @throws SvcLogicException + */ + private boolean isValueNsForLeaf(List cont, Element element) + throws SvcLogicException { + for (Object c : cont) { + if (c instanceof Namespace) { + String value = element.getText(); + if (value != null) { + String[] val = value.split(":"); + String valPrefix = val[0]; + String actVal = val[1]; + if (valPrefix != null && actVal != null && + valPrefix.equals(((Namespace) c).getPrefix())) { + serializerHelper.addNode( + element.getName(), + element.getNamespace().getURI(), + actVal, + ((Namespace) c).getURI(), null); + return true; + } + } + } + } + return false; + } + + @Override + public void exitXmlElement(Element element) throws SvcLogicException { + serializerHelper.exitNode(); + } + + @Override + public SerializerHelper serializerHelper() { + return serializerHelper; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlWalker.java new file mode 100644 index 000000000..cdc713f31 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DefaultXmlWalker.java @@ -0,0 +1,48 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Element; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import java.util.Iterator; + +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.getXmlNodeType; + +/** + * Implementation of XML walker to walk through the nodes and process it. + */ +public class DefaultXmlWalker implements XmlWalker { + + @Override + public void walk(XmlListener listener, Element xmlElement) throws + SvcLogicException { + listener.enterXmlElement(xmlElement, getXmlNodeType(xmlElement)); + if (xmlElement.hasContent() && !xmlElement.isTextOnly()) { + Iterator i = xmlElement.elementIterator(); + while (i.hasNext()) { + Element childElement = (Element) i.next(); + walk(listener, childElement); + } + } + listener.exitXmlElement(xmlElement); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfListenerFactory.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfListenerFactory.java new file mode 100644 index 000000000..06a811e15 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfListenerFactory.java @@ -0,0 +1,87 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.FORMAT_ERR; + +/** + * Represents the data format listener factory which will return JSON or XML + * listener according to the serializer helper. + */ +public final class DfListenerFactory { + + /** + * Creates a new DfListenerFactory. + */ + private DfListenerFactory() { + } + + /** + * Returns the instance of the data format listener factory. + * + * @return instance of the data format listener factory + */ + public static DfListenerFactory instance() { + return DfListenerFactory.LazyHolder.INSTANCE; + } + + /** + * Bill pugh singleton pattern. Instance will not be instantiated until + * the lazy holder class is loaded via a call to the instance of method + * below. + */ + private static class LazyHolder { + private static final DfListenerFactory INSTANCE = + new DfListenerFactory(); + } + + /** + * Returns the data format listener by deciding it based on the format of + * the parameter. + * + * @param serHelper serializer helper + * @param params parameters + * @return data format listener + * @throws SvcLogicException when the data format type is wrong + */ + public Listener getListener(SerializerHelper serHelper, + YangParameters params) + throws SvcLogicException { + Listener listener; + switch (params.format) { + case JSON: + listener = new DefaultJsonListener(serHelper); + break; + + case XML: + listener = new DefaultXmlListener(serHelper); + break; + + default: + throw new SvcLogicException(format(FORMAT_ERR, + params.format)); + } + return listener; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerFactory.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerFactory.java new file mode 100644 index 000000000..4d95235e5 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerFactory.java @@ -0,0 +1,87 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.FORMAT_ERR; + +/** + * Represents the data format serializer factory which will return JSON or XML + * serializer according to the serializer context. + */ +public final class DfSerializerFactory { + + /** + * Creates a new DfSerializerFactory. + */ + private DfSerializerFactory() { + } + + /** + * Returns the instance of the data format serializer factory. + * + * @return instance of the data format serializer factory + */ + public static DfSerializerFactory instance() { + return DfSerializerFactory.LazyHolder.INSTANCE; + } + + /** + * Bill pugh singleton pattern. Instance will not be instantiated until + * the lazy holder class is loaded via a call to the instance of method + * below. + */ + private static class LazyHolder { + private static final DfSerializerFactory INSTANCE = + new DfSerializerFactory(); + } + + /** + * Returns the data format serializer by deciding it based on the format of + * the parameter. + * + * @param serCtx serializer context + * @param params parameters + * @return data format serializer + * @throws SvcLogicException when the data format type is wrong + */ + public DataFormatSerializer getSerializer(DataFormatSerializerContext serCtx, + YangParameters params) + throws SvcLogicException { + DataFormatSerializer serializer; + switch (params.format) { + case JSON: + serializer = new JsonSerializer(serCtx); + break; + + case XML: + serializer = new XmlSerializer(serCtx); + break; + + default: + throw new SvcLogicException(format(FORMAT_ERR, + params.format)); + } + return serializer; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java new file mode 100644 index 000000000..707c29444 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DfSerializerUtil.java @@ -0,0 +1,231 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Element; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Iterator; + +import static javax.xml.transform.OutputKeys.INDENT; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.XmlNodeType.OBJECT_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.XmlNodeType.TEXT_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getRevision; + +/** + * Utilities for data format serializer. + */ +public final class DfSerializerUtil { + + static final String JSON_WRITE_ERR = "Unable to write to JSON from " + + "properties."; + + static final String NODE_TYPE_ERR = "The node type %s is not supported."; + + static final String JSON_LIS_ERR = "The JSON serializer doesn't have " + + "JSON listener"; + + static final String XML_LIS_ERR = "The XML serializer doesn't have XML " + + "listener"; + + static final String PROP_NODE_ERR = "The property node doesn't have " + + "schema node bound to it."; + + static final String DF_ERR = "Type mismatch for the node %s. The schema " + + "node does not match with the data format node type %s."; + + static final String XML_PREFIX = "yangid"; + + private static final String YES = "yes"; + + private static final String INDENT_XMLNS = "{http://xml.apache" + + ".org/xslt}indent-amount"; + + private static final String XML_PARSE_ERR = "Unable to parse the xml to " + + "document : \n"; + + private static final String URI_ERR = "Unable to parse the URI"; + + /** + * Data format error message for unsupported types. + */ + public static final String FORMAT_ERR = "Only JSON and XML formats are " + + "supported. %s is not supported"; + + /** + * UTF header message for XML data format message. + */ + public static final String UTF_HEADER = "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\"?>"; + + /** + * Error message when a JSON tree creation fails. + */ + public static final String JSON_TREE_ERR = "Unable to form JSON tree " + + "object from the JSON body provided."; + + /** + * Error message when a XML tree creation fails. + */ + public static final String XML_TREE_ERR = "Unable to form XML tree object" + + " from the XML body provided."; + + //No instantiation. + private DfSerializerUtil() { + } + + /** + * Returns the writer which contains the pretty formatted XML string. + * + * @param input input XML + * @param indent indentation level + * @return writer with XML + * @throws SvcLogicException when transformation of source fails + */ + public static Writer getXmlWriter(String input, String indent) + throws SvcLogicException { + try { + Transformer transformer = TransformerFactory.newInstance() + .newTransformer(); + transformer.setOutputProperty(INDENT, YES); + transformer.setOutputProperty(INDENT_XMLNS, indent); + StreamResult result = new StreamResult(new StringWriter()); + DOMSource source = new DOMSource(parseXml(input)); + transformer.transform(source, result); + return result.getWriter(); + } catch (TransformerException e) { + throw new SvcLogicException(XML_PARSE_ERR + input, e); + } + } + + /** + * Parses the XML and converts it into dom document which can be used for + * formatting the XML. + * + * @param in input XML + * @return dom document of XML + * @throws SvcLogicException when document building fails + */ + private static Document parseXml(String in) throws SvcLogicException { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + try { + db = dbf.newDocumentBuilder(); + InputSource is = new InputSource(new StringReader(in)); + return db.parse(is); + } catch (SAXException | IOException | ParserConfigurationException e) { + throw new SvcLogicException(XML_PARSE_ERR + in, e); + } + } + + /** + * Returns the resolved namespace object from the input received from the + * abstract data format. + * + * @param mName module name + * @param mUri module URI + * @param ctx schema context + * @param parent parent properties node + * @return namespace + * @throws SvcLogicException when resolving namespace fails + */ + static Namespace getResolvedNamespace(String mName, String mUri, + SchemaContext ctx, + PropertiesNode parent) + throws SvcLogicException { + if (mName == null && mUri == null) { + Namespace parentNs = parent.namespace(); + return new Namespace(parentNs.moduleName(), parentNs.moduleNs(), + parentNs.revision()); + } + + Iterator<Module> it; + Module mod; + if (mName != null) { + it = ctx.findModules(mName).iterator(); + } else { + URI modUri = null; + try { + modUri = new URI(mUri); + } catch (URISyntaxException e) { + throw new SvcLogicException(URI_ERR, e); + } + it = ctx.findModules(modUri).iterator(); + } + + if (!it.hasNext()) { + return null; + } + mod = it.next(); + + return new Namespace(mod.getName(), mod.getQNameModule().getNamespace(), + getRevision(mod.getRevision())); + } + + /** + * Returns the node type of a XML element. + * + * @param element XML element + * @return node type of the XML element + */ + static XmlNodeType getXmlNodeType(Element element) { + Element newElement = element.createCopy(); + newElement.remove(element.getNamespace()); + return newElement.hasContent() && newElement.isTextOnly() ? + TEXT_NODE : OBJECT_NODE; + } + + /** + * Resolves the super type to the base type from type definition. + * + * @param type super type + * @return base type definition + */ + static TypeDefinition<?> resolveBaseTypeFrom(TypeDefinition<?> type) { + TypeDefinition superType = type; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + return superType; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonListener.java new file mode 100644 index 000000000..89fd4c8c8 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonListener.java @@ -0,0 +1,55 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.fasterxml.jackson.databind.JsonNode; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType; + +/** + * Abstraction of an entity which provides call back methods, which in turn + * are called by JSON walker while walking the JSON tree. This interface + * needs to be implemented by protocol, implementing listener based call + * while doing JSON walk. + */ +public interface JsonListener extends Listener { + + /** + * Call back invoked during JSON node entry. All other related + * information can be obtained from the JSON node. + * + * @param nodeName JSON node name + * @param node JSON node + * @param nodeType JSON node type + * @throws SvcLogicException when node type is of wrong format + */ + void enterJsonNode(String nodeName, JsonNode node, NodeType nodeType) + throws SvcLogicException; + + /** + * Call back invoked during JSON node exit. All the related information + * can be obtained from the JSON node. + * + * @param node JSON node + * @throws SvcLogicException when JSON node exit doesn't happen + */ + void exitJsonNode(JsonNode node) throws SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonSerializer.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonSerializer.java new file mode 100644 index 000000000..91adb8126 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonSerializer.java @@ -0,0 +1,91 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.DefaultPropertiesNodeWalker; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeWalker; + +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormat.JSON; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.JSON_LIS_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.JSON_TREE_ERR; + +/** + * Representation of JSON serializer which encodes properties to JSON and + * decodes properties from JSON with the data format serializer. + */ +public class JsonSerializer extends DataFormatSerializer { + + /** + * Creates an instance of data format serializer. + * + * @param serializerContext data format serializer context + */ + protected JsonSerializer(DataFormatSerializerContext serializerContext) { + super(JSON, serializerContext); + } + + @Override + public String encode(Map<String, String> param, + Map<String, List<Annotation>> annotations) + throws SvcLogicException { + PropertiesNode propNode = serializerContext().getPropNodeSerializer() + .encode(param); + PropertiesNodeWalker nodeWalker = new DefaultPropertiesNodeWalker<>(); + PropertiesNodeJsonListener jsonLis = new PropertiesNodeJsonListener(); + nodeWalker.walk(jsonLis, propNode); + Writer writer = jsonLis.getWriter(); + return writer.toString(); + } + + @Override + public Map<String, String> decode(String dataFormatBody) + throws SvcLogicException { + if (!(serializerContext().listener() instanceof JsonListener)) { + throw new SvcLogicException(JSON_LIS_ERR); + } + + JsonListener listener = (JsonListener) serializerContext().listener(); + JsonWalker walker = new DefaultJsonWalker(); + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode; + + try { + jsonNode = mapper.readTree(dataFormatBody); + } catch (IOException e) { + throw new SvcLogicException(JSON_TREE_ERR, e); + } + + walker.walk(listener, jsonNode); + + return serializerContext().getPropNodeSerializer().decode( + listener.serializerHelper().getPropertiesNode()); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonWalker.java new file mode 100644 index 000000000..588070a12 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/JsonWalker.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.fasterxml.jackson.databind.JsonNode; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of an entity which provides interface for JSON walk. This + * interface serves as a common tool for anyone who needs to parse the JSON + * node with depth-first algorithm. + */ +public interface JsonWalker { + + /** + * Walks the JSON data tree. Protocols implement JSON listener service + * and walks the JSON tree with input as implemented object. JSON walker + * provides call back to the implemented methods. + * + * @param listener JSON listener implemented by the protocol + * @param jsonNode root node of the JSON data tree + * @throws SvcLogicException when walking the JSON node fails + */ + void walk(JsonListener listener, JsonNode jsonNode) + throws SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Listener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Listener.java new file mode 100644 index 000000000..e5812dbb7 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/Listener.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +/** + * Abstraction of listener. + */ +public interface Listener { + + /** + * Returns serializer helper for this listener. + * + * @return serializer helper + */ + SerializerHelper serializerHelper(); +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java new file mode 100644 index 000000000..1fd0d2dee --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/MdsalSerializerHelper.java @@ -0,0 +1,297 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.RootNode; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; + +import java.util.Deque; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.DF_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.NODE_TYPE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.PROP_NODE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.getResolvedNamespace; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.resolveBaseTypeFrom; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getRevision; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_HOLDER_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_HOLDER_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_NODE; +import static org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils.findDataChildSchemaByQName; +import static org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils.findSchemaForChild; +import static org.opendaylight.yangtools.yang.data.util.ParserStreamUtils.findSchemaNodeByNameAndNamespace; + +/** + * Representation of MDSAL based serializer helper, which adds properties + * node to the properties tree based on its types. + */ +public class MdsalSerializerHelper extends SerializerHelper<SchemaNode, SchemaContext> { + + /** + * Current properties node. + */ + private PropertiesNode propNode; + + /** + * Current schema node. + */ + private SchemaNode curSchemaNode; + + + /** + * Creates MDSAL serializer helper with root schema node, schema context + * and URI. + * + * @param n schema node of the URI's last node + * @param c schema context + * @param u URI of the request + */ + public MdsalSerializerHelper(SchemaNode n, SchemaContext c, + String u) { + super(n, c, u); + Namespace ns = new Namespace(n.getQName().getLocalName(), + n.getQName().getNamespace(), + getRevision(n.getQName().getRevision())); + propNode = new RootNode<>(n.getQName().getLocalName(), ns, + getSchemaNode(), u); + curSchemaNode = getSchemaNode(); + } + + @Override + protected SchemaNode getSchemaNode() { + return schemaNode; + } + + @Override + protected SchemaContext getSchemaCtx() { + return schemaCtx; + } + + @Override + protected SchemaNode getCurSchema() { + return curSchemaNode; + } + + @Override + protected void addNode(String name, String nameSpace, String value, + String valNameSpace, NodeType type) + throws SvcLogicException { + Namespace ns; + if (type == null) { + ns = getResolvedNamespace(null, nameSpace, + getSchemaCtx(), propNode); + } else { + ns = getResolvedNamespace(nameSpace, null, + getSchemaCtx(), propNode); + } + if (isChildPresent(name, ns)) { + addNodeToProperty(name, ns, value, valNameSpace, type); + } else { + throw new SvcLogicException(format( + "Unable to add the node %s", name)); + } + } + + @Override + protected void exitNode() throws SvcLogicException { + propNode = propNode.parent(); + if (propNode != null) { + NodeType type = propNode.nodeType(); + if (type == MULTI_INSTANCE_HOLDER_NODE || + type == MULTI_INSTANCE_LEAF_HOLDER_NODE) { + propNode = propNode.parent(); + } + } + if (propNode == null || propNode.appInfo() == null + || !(propNode.appInfo() instanceof SchemaNode)) { + throw new SvcLogicException(PROP_NODE_ERR); + } + curSchemaNode = (SchemaNode) propNode.appInfo(); + } + + @Override + protected PropertiesNode getPropertiesNode() { + return propNode; + } + + /** + * Adds the node to property node based on the type of the schema node, + * which is decided based on the name and namespace of the input + * information. + * + * @param name name of the node + * @param ns namespace of the node + * @param value value of the node if its a leaf/leaf-list + * @param valNamespace namespace of the value + * @param type type of the node + * @throws SvcLogicException when adding child fails + */ + private void addNodeToProperty(String name, Namespace ns, String value, + String valNamespace, NodeType type) + throws SvcLogicException { + Namespace valueNs; + if (type != null) { + validateNodeType(type); + } + if (curSchemaNode instanceof LeafSchemaNode) { + valueNs = getValueNs(curSchemaNode, valNamespace, type); + propNode = propNode.addChild(name, ns, + SINGLE_INSTANCE_LEAF_NODE, + value, valueNs, curSchemaNode); + } else if (curSchemaNode instanceof LeafListSchemaNode) { + valueNs = getValueNs(curSchemaNode, valNamespace, type); + propNode = propNode.addChild(null, name, ns, + MULTI_INSTANCE_LEAF_NODE, value, + valueNs, curSchemaNode); + } else if (curSchemaNode instanceof ListSchemaNode) { + propNode = propNode.addChild(null, name, ns, MULTI_INSTANCE_NODE, + curSchemaNode); + } else { + propNode = propNode.addChild(name, ns, SINGLE_INSTANCE_NODE, + curSchemaNode); + } + } + + /** + * Returns the namespace of the value namespace in case of identity ref. + * + * @param schemaNode schema node + * @param valNs value name space + * @param nodeType node type + * @return namespace of value namespace + * @throws SvcLogicException when namespace resolution fails for identityref + */ + private Namespace getValueNs(SchemaNode schemaNode, String valNs, + NodeType nodeType) throws SvcLogicException { + Namespace ns = null; + if (valNs != null) { + TypeDefinition type; + if (schemaNode instanceof LeafSchemaNode) { + type = ((LeafSchemaNode) schemaNode).getType(); + } else { + type = ((LeafListSchemaNode) schemaNode).getType(); + } + TypeDefinition<?> baseType = resolveBaseTypeFrom(type); + if (baseType instanceof IdentityrefTypeDefinition) { + if (nodeType == null) { + ns = getResolvedNamespace(null, valNs, getSchemaCtx(), + propNode); + } else { + ns = getResolvedNamespace(valNs, null, getSchemaCtx(), + propNode); + } + } + } + return ns; + } + + /** + * Validates that the node type from the data format matches with that of + * the corresponding schema node. + * + * @param type node type from the abstract data format + * @throws SvcLogicException when the node type is wrong + */ + private void validateNodeType(NodeType type) throws SvcLogicException { + boolean verify; + switch (type) { + case SINGLE_INSTANCE_LEAF_NODE: + verify = curSchemaNode instanceof LeafSchemaNode; + break; + + case MULTI_INSTANCE_LEAF_NODE: + verify = curSchemaNode instanceof LeafListSchemaNode; + break; + + case MULTI_INSTANCE_NODE: + verify = curSchemaNode instanceof ListSchemaNode; + break; + + case SINGLE_INSTANCE_NODE: + verify = (!(curSchemaNode instanceof LeafSchemaNode) && + !(curSchemaNode instanceof LeafListSchemaNode) && + !(curSchemaNode instanceof ListSchemaNode)); + break; + + default: + throw new SvcLogicException(format(NODE_TYPE_ERR, + type.toString())); + } + if (!verify) { + throw new SvcLogicException(format(DF_ERR, curSchemaNode + .getQName().getLocalName(), type.toString())); + } + } + + /** + * Returns true if the child schema is present with the name and + * namespace inside the current schema node, if present updates the + * current schema node; false otherwise. + * + * @param name name of the child schema node + * @param namespace namespace of the child schema node + * @return returns true if the child schema is available; false otherwise + */ + private boolean isChildPresent(String name, Namespace namespace) { + QName qname = QName.create(namespace.moduleNs(), + Revision.of(namespace.revision()), name); + SchemaNode childNode = null; + if (curSchemaNode instanceof DataSchemaNode) { + Deque<DataSchemaNode> dataSchema = findSchemaNodeByNameAndNamespace( + (DataSchemaNode) curSchemaNode, name, namespace.moduleNs()); + + if (dataSchema != null && !dataSchema.isEmpty()) { + childNode = dataSchema.pop(); + } + + if (dataSchema != null && !dataSchema.isEmpty()) { + childNode = findSchemaForChild(((ChoiceSchemaNode) childNode), + qname); + } + + } else { + childNode = findDataChildSchemaByQName(curSchemaNode, qname); + } + + if (childNode != null) { + curSchemaNode = childNode; + return true; + } + return false; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeJsonListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeJsonListener.java new file mode 100644 index 000000000..0f03039e5 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeJsonListener.java @@ -0,0 +1,233 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Collection; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.DefaultPropertiesNodeWalker; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.LeafNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeListener; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.RootNode; + +import static com.google.common.base.Strings.repeat; +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.JSON_WRITE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.NODE_TYPE_ERR; + +/** + * Representation of JSON implementation of properties node listener. + */ +public class PropertiesNodeJsonListener implements PropertiesNodeListener{ + + /** + * JSON writer to write the JSON data format. + */ + private JsonWriter jsonWriter; + + /** + * Writer to write the JSON. + */ + private Writer writer; + + /** + * Creates the properties node JSON listener by instantiating and + * indenting the writer. + */ + public PropertiesNodeJsonListener() { + writer = new StringWriter(); + jsonWriter = new JsonWriter(writer); + jsonWriter.setIndent(repeat(" ", 4)); + } + + @Override + public void start(PropertiesNode node) throws SvcLogicException { + try { + jsonWriter.beginObject(); + } catch (IOException e) { + throw new SvcLogicException(JSON_WRITE_ERR, e); + } + } + + @Override + public void end(PropertiesNode node) throws SvcLogicException { + try { + jsonWriter.endObject(); + jsonWriter.flush(); + } catch (IOException e) { + throw new SvcLogicException(JSON_WRITE_ERR, e); + } + } + + @Override + public void enterPropertiesNode(PropertiesNode node) + throws SvcLogicException { + String val; + String nodeName = getNodeName(node); + try { + switch (node.nodeType()) { + case SINGLE_INSTANCE_NODE: + jsonWriter.name(nodeName); + jsonWriter.beginObject(); + break; + + case MULTI_INSTANCE_NODE: + jsonWriter.beginObject(); + break; + + case SINGLE_INSTANCE_LEAF_NODE: + val = getValueWithNs((LeafNode) node); + jsonWriter.name(nodeName).value(val); + break; + + case MULTI_INSTANCE_HOLDER_NODE: + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + jsonWriter.name(nodeName); + jsonWriter.beginArray(); + break; + + case MULTI_INSTANCE_LEAF_NODE: + val = getValueWithNs((LeafNode) node); + jsonWriter.value(val); + break; + + case ANY_XML_NODE: + jsonWriter.name(nodeName); + val = ((LeafNode) node).value(); + try { + jsonWriter.jsonValue(val); + } catch (IOException e) { + throw new SvcLogicException(JSON_WRITE_ERR, e); + } + break; + + default: + throw new SvcLogicException(format( + NODE_TYPE_ERR, node.nodeType().toString())); + + } + } catch (IOException e) { + throw new SvcLogicException(JSON_WRITE_ERR, e); + } + } + + @Override + public void exitPropertiesNode(PropertiesNode node) throws SvcLogicException { + walkAugmentationNode(node); + try { + switch (node.nodeType()) { + case SINGLE_INSTANCE_NODE: + case MULTI_INSTANCE_NODE: + jsonWriter.endObject(); + break; + + case MULTI_INSTANCE_HOLDER_NODE: + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + jsonWriter.endArray(); + break; + + case SINGLE_INSTANCE_LEAF_NODE: + case MULTI_INSTANCE_LEAF_NODE: + case ANY_XML_NODE: + break; + + default: + throw new SvcLogicException(format( + NODE_TYPE_ERR, node.nodeType().toString())); + } + } catch (IOException e) { + throw new SvcLogicException(JSON_WRITE_ERR, e); + } + } + + /** + * Returns the writer. + * + * @return writer + */ + public Writer getWriter() { + return writer; + } + + /** + * Returns the abstract JSON node name to be used in JSON data format + * from the properties node. + * + * @param node properties node + * @return abstract JSON node + */ + private String getNodeName(PropertiesNode node) { + PropertiesNode parent = node.parent(); + if (parent instanceof RootNode || !parent.namespace().moduleName() + .equals(node.namespace().moduleName())) { + if (!parent.nonAppend()) { + return node.namespace().moduleName() + ":" + node.name(); + } + } + return node.name(); + } + + /** + * Returns the value of JSON leaf node with module name if required. + * + * @param node properties node + * @return value with namespace + */ + private String getValueWithNs(LeafNode node) { + Namespace valNs = node.valueNs(); + String modName = (valNs == null) ? null : valNs.moduleName(); + if (modName != null) { + return modName + ":" + node.value(); + } + return node.value(); + } + + /** + * Gets all the augmentation of the given node and walks through it. + * + * @param node properties node + * @throws SvcLogicException when walking the properties node fails + */ + private void walkAugmentationNode(PropertiesNode node) + throws SvcLogicException { + for (Map.Entry<Object, Collection<PropertiesNode>> + augToChild : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> augChild = augToChild.getValue(); + if (!augChild.isEmpty()) { + DefaultPropertiesNodeWalker walker = new + DefaultPropertiesNodeWalker(); + for (PropertiesNode p : augChild) { + enterPropertiesNode(p); + walker.walkChildNode(this, p); + exitPropertiesNode(p); + } + } + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeXmlListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeXmlListener.java new file mode 100644 index 000000000..f098195e4 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/PropertiesNodeXmlListener.java @@ -0,0 +1,246 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.DefaultPropertiesNodeWalker; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.LeafNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.Namespace; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeListener; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.RootNode; + +import java.io.Writer; +import java.net.URI; +import java.util.Collection; +import java.util.Map; +import java.util.Stack; + +import static java.lang.String.format; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.NODE_TYPE_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.UTF_HEADER; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.XML_PREFIX; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.getXmlWriter; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_HOLDER_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_HOLDER_NODE; + +/** + * Representation of XML implementation of properties node listener. + */ +public class PropertiesNodeXmlListener implements PropertiesNodeListener { + + /** + * XML data from the element. + */ + private String xmlData; + + /** + * Root element of the XML document. + */ + private Element rootElement; + + /** + * Writer to write the XML. + */ + private Writer writer; + + /** + * XML element stack to store the elements. + */ + private final Stack<Element> elementStack = new Stack<>(); + + /** + * Creates the properties node XML listener. + */ + public PropertiesNodeXmlListener() { + } + + @Override + public void start(PropertiesNode node) { + rootElement = addElement(null, node); + elementStack.push(rootElement); + } + + @Override + public void end(PropertiesNode node) throws SvcLogicException { + xmlData = rootElement.asXML(); + xmlData = UTF_HEADER + xmlData; + writer = getXmlWriter(xmlData, "4"); + } + + @Override + public void enterPropertiesNode(PropertiesNode node) + throws SvcLogicException { + Element element = null; + String ns = getNodeNamespace(node); + switch (node.nodeType()) { + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + case MULTI_INSTANCE_HOLDER_NODE: + break; + + case SINGLE_INSTANCE_NODE: + case MULTI_INSTANCE_NODE: + element = addElement(ns, node); + break; + + case MULTI_INSTANCE_LEAF_NODE: + case SINGLE_INSTANCE_LEAF_NODE: + element = addElement(ns, node); + setValueWithNs(element, (LeafNode) node); + break; + + default: + throw new SvcLogicException(format( + NODE_TYPE_ERR, node.nodeType().toString())); + } + if (element != null) { + elementStack.push(element); + } + } + + @Override + public void exitPropertiesNode(PropertiesNode node) + throws SvcLogicException { + walkAugmentationNode(node); + switch (node.nodeType()) { + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + case MULTI_INSTANCE_HOLDER_NODE: + break; + + case SINGLE_INSTANCE_NODE: + case MULTI_INSTANCE_NODE: + case MULTI_INSTANCE_LEAF_NODE: + case SINGLE_INSTANCE_LEAF_NODE: + if (!elementStack.isEmpty()) { + elementStack.pop(); + } + break; + + default: + throw new SvcLogicException(format( + NODE_TYPE_ERR, node.nodeType().toString())); + } + } + + /** + * Returns the writer. + * + * @return writer + */ + public Writer getWriter() { + return writer; + } + + /** + * Adds an XML element to the stack with namespace if present. If the + * stack is empty it creates new document and adds element else adds to + * the parent element. + * + * @param ns namespace of the element + * @param node properties node + * @return new added element + */ + private Element addElement(String ns, PropertiesNode node) { + Element element; + if (elementStack.isEmpty()) { + Document doc = DocumentHelper.createDocument(); + if (ns != null) { + element = doc.addElement(node.name(), ns); + } else { + element = doc.addElement(node.name()); + } + } else { + element = elementStack.peek(); + if (ns != null) { + element = element.addElement(node.name(), ns); + } else { + element = element.addElement(node.name()); + } + } + + return element; + } + + /** + * Returns the abstract XML namespace to be used in XML data format from + * the properties node. + * + * @param node properties node + * @return abstract XML namespace + */ + private String getNodeNamespace(PropertiesNode node) { + PropertiesNode parent = node.parent(); + if (parent.nodeType() == MULTI_INSTANCE_HOLDER_NODE || + parent.nodeType() == MULTI_INSTANCE_LEAF_HOLDER_NODE) { + parent = parent.parent(); + } + if (parent instanceof RootNode || ! parent.namespace().moduleName() + .equals(node.namespace().moduleName())) { + return node.namespace().moduleNs().toString(); + } + return null; + } + + /** + * Sets the value to the element for a leaf node and adds the value + * namespace if required. + * + * @param element XML element + * @param node leaf properties node + */ + private void setValueWithNs(Element element, LeafNode node) { + Namespace valNs = node.valueNs(); + URI modNs = (valNs == null) ? null : valNs.moduleNs(); + String val = node.value(); + if (modNs != null) { + element.addNamespace(XML_PREFIX, modNs.toString()); + element.setText(XML_PREFIX + ":" + val); + } else { + element.setText(val); + } + } + + /** + * Gets all the augmentation of the given node and walks through it. + * + * @param node properties node + * @throws SvcLogicException when walking the properties node fails + */ + private void walkAugmentationNode(PropertiesNode node) + throws SvcLogicException { + for (Map.Entry<Object, Collection<PropertiesNode>> + augToChild : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> augChild = augToChild.getValue(); + if (!augChild.isEmpty()) { + DefaultPropertiesNodeWalker walker = new + DefaultPropertiesNodeWalker(); + for (PropertiesNode p : augChild) { + enterPropertiesNode(p); + walker.walkChildNode(this, p); + exitPropertiesNode(p); + } + } + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/SerializerHelper.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/SerializerHelper.java new file mode 100644 index 000000000..db9befb21 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/SerializerHelper.java @@ -0,0 +1,116 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; + +/** + * Abstraction of an entity which helps the data format serializers to obtain + * schema context details and to build properties from data. + * + * @param <T> type of schema node + * @param <P> type of schema context + */ +public abstract class SerializerHelper<T, P> { + + /** + * Schema node of the last element in the URI. + */ + protected T schemaNode; + + /** + * Root schema context. + */ + protected P schemaCtx; + + /** + * Root URI. + */ + protected String rootUri; + + /** + * Creates an instance of the serializer helper with the schema node, + * schema context and the URI. + * + * @param t schema node + * @param p schema context + * @param u root URI + */ + protected SerializerHelper(T t, P p, String u) { + schemaNode = t; + schemaCtx = p; + rootUri = u; + } + + /** + * Returns schema node of the last element in the URI. + * + * @return schema node + */ + protected abstract T getSchemaNode(); + + /** + * Returns the root schema context. + * + * @return schema context + */ + protected abstract P getSchemaCtx(); + + /** + * Returns the current schema context node. + * + * @return current schema context node + */ + protected abstract T getCurSchema(); + + /** + * Adds a node to the properties node tree. + * + * @param name name of the node + * @param nameSpace name space of the node, it can be either module + * name or namespace; null indicates parent namespace + * @param value value of the node; applicable for leaf/leaf-list node + * @param valNameSpace value namespace for identityref, could be module + * name or namespace + * @param type type of node if known like in case of JSON + * @throws SvcLogicException when adding node fails + */ + protected abstract void addNode(String name, String nameSpace, String value, + String valNameSpace, NodeType type) + throws SvcLogicException; + + /** + * Exits the node, in case if it's leaf node then it adds to the properties + * map. + * + * @throws SvcLogicException when properties node tree is improper + */ + protected abstract void exitNode() throws SvcLogicException; + + /** + * Returns the built properties corresponding to the data. + * + * @return properties node. + */ + protected abstract PropertiesNode getPropertiesNode(); +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlListener.java new file mode 100644 index 000000000..784e7af1f --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlListener.java @@ -0,0 +1,53 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Element; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of an entity which provides call back methods, which in turn + * are called by XML walker while walking the XML tree. This interface needs + * to be implemented by protocol implementing listener based call while doing + * XML walk. + */ +public interface XmlListener extends Listener { + + /** + * Callback invoked during a node entry. All the related information + * about the node can be obtained from the element. + * + * @param element current XML element + * @param nodeType node type of the element + * @throws SvcLogicException when node type is of wrong format + */ + void enterXmlElement(Element element, XmlNodeType nodeType) + throws SvcLogicException; + + /** + * Callback invoked during a node exit. All the related information about + * the node can be obtained from the element. + * + * @param element current xml element. + * @throws SvcLogicException when XML node exit doesn't happen + */ + void exitXmlElement(Element element) throws SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlNodeType.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlNodeType.java new file mode 100644 index 000000000..7a6db3c86 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlNodeType.java @@ -0,0 +1,37 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +/** + * Represents the XML node type. + */ +public enum XmlNodeType { + + /** + * Object XML node type. + */ + OBJECT_NODE, + + /** + * Text XML node type. + */ + TEXT_NODE +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlSerializer.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlSerializer.java new file mode 100644 index 000000000..f3c6723e1 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlSerializer.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.DefaultPropertiesNodeWalker; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNode; +import org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.PropertiesNodeWalker; + +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormat.XML; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.XML_LIS_ERR; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DfSerializerUtil.XML_TREE_ERR; + +/** + * Representation of XML serializer which encodes properties to XML and + * decodes properties from XML with the data format serializer. + */ +public class XmlSerializer extends DataFormatSerializer { + + /** + * Creates an instance of XML serializer. + * + * @param serializerContext data format serializer context + */ + protected XmlSerializer(DataFormatSerializerContext serializerContext) { + super(XML, serializerContext); + } + + @Override + public String encode(Map<String, String> param, + Map<String, List<Annotation>> annotations) + throws SvcLogicException { + PropertiesNode propNode = serializerContext().getPropNodeSerializer() + .encode(param); + PropertiesNodeWalker nodeWalker = new DefaultPropertiesNodeWalker<>(); + PropertiesNodeXmlListener xmlListener = new PropertiesNodeXmlListener(); + nodeWalker.walk(xmlListener, propNode); + Writer writer = xmlListener.getWriter(); + return removeRootNode(writer.toString(), propNode.name()); + } + + @Override + public Map<String, String> decode(String dataFormatBody) + throws SvcLogicException { + if (!(serializerContext().listener() instanceof XmlListener)) { + throw new SvcLogicException(XML_LIS_ERR); + } + + XmlListener listener = (XmlListener) serializerContext().listener(); + XmlWalker walker = new DefaultXmlWalker(); + Document document; + + try { + document = DocumentHelper.parseText(dataFormatBody); + } catch (DocumentException e) { + throw new SvcLogicException(XML_TREE_ERR, e); + } + walker.walk(listener, document.getRootElement()); + + return serializerContext().getPropNodeSerializer().decode( + listener.serializerHelper().getPropertiesNode()); + } + + /** + * Removes root node from the XML data format message and makes the + * string to be pretty print. + * + * @param xml XML data format message + * @param rootName root node name + * @return pretty print format XML message + */ + private static String removeRootNode(String xml, String rootName) { + xml = xml.replace("\n<" + rootName + ">", "\n"); + xml = xml.replace("</" + rootName + ">" + "\n", ""); + xml = xml.replaceAll("\n" + " ", "\n"); + xml = xml.replaceFirst("\n", ""); + return xml; + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlWalker.java new file mode 100644 index 000000000..3835faa7e --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/XmlWalker.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.dom4j.Element; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of an entity which provides interface for XML walk. This + * interface serves as a common tool for anyone who needs to parse the XML + * node with depth-first algorithm. + */ +public interface XmlWalker { + + /** + * Walks the XML data tree. Protocols implement XML listener service and + * walks the XML tree with input as implemented object. XML walker + * provides call back to the implemented methods. + * + * @param listener XML listener implemented by the protocol + * @param xmlElement root element of the XML data tree + * @throws SvcLogicException when walking the XML node fails + */ + void walk(XmlListener listener, Element xmlElement) throws + SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/YangParameters.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/YangParameters.java new file mode 100644 index 000000000..8eb3fb6e8 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/YangParameters.java @@ -0,0 +1,42 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import org.onap.ccsdk.sli.plugins.restapicall.Parameters; + +/** + * Representation of the YANG parameters for the restconf api call node. + */ +public class YangParameters extends Parameters { + /** + * Directory path of the YANG file. + */ + public String dirPath; + + /** + * Creates an instance of the YANG parameters. + */ + public YangParameters() { + super(); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DataNodeChild.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DataNodeChild.java new file mode 100644 index 000000000..7be54690a --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DataNodeChild.java @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Abstraction of entity representing child's to data node. + */ +public interface DataNodeChild extends NodeChild { +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeListener.java new file mode 100644 index 000000000..2fa00bfb7 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeListener.java @@ -0,0 +1,105 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.COLON; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.UNDERSCORE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_LEAF_NODE; + +/** + * Represents implementation of PropertiesNodeListener. + */ +public class DefaultPropertiesNodeListener implements PropertiesNodeListener { + + private Map<String, String> params = new HashMap<>(); + + @Override + public void start(PropertiesNode node) { + // do nothing + } + + @Override + public void end(PropertiesNode node) throws SvcLogicException { + exitPropertiesNode(node); + } + + @Override + public void enterPropertiesNode(PropertiesNode node) { + /* + * Only if it is leaf node or leaf-list node, + * then create a property entry and add to map + */ + if (node.nodeType() == SINGLE_INSTANCE_LEAF_NODE + || node.nodeType() == MULTI_INSTANCE_LEAF_NODE) { + String val = ((LeafNode) node).value(); + if (((LeafNode) node).valueNs() != null) { + val = ((LeafNode) node).valueNs().moduleName() + COLON + val; + } + String uri = node.uri().replaceAll(COLON, UNDERSCORE); + params.put(uri, val); + } + } + + @Override + public void exitPropertiesNode(PropertiesNode node) throws + SvcLogicException { + if (!node.augmentations().isEmpty()) { + for (Map.Entry<Object, Collection<PropertiesNode>> augmentationTochild + : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> childsFromAugmentations = augmentationTochild + .getValue(); + if (!childsFromAugmentations.isEmpty()) { + PropertiesNodeWalker walker = new DefaultPropertiesNodeWalker<>(); + for (PropertiesNode pNode : childsFromAugmentations) { + enterPropertiesNode(pNode); + walker.walk(this, pNode); + exitPropertiesNode(pNode); + } + } + } + } + } + + /** + * Returns properties. + * + * @return properties + */ + public Map<String, String> params() { + return params; + } + + /** + * Sets properties. + * + * @param params properties + */ + public void params(Map<String, String> params) { + this.params = params; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeWalker.java new file mode 100644 index 000000000..5034530d5 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/DefaultPropertiesNodeWalker.java @@ -0,0 +1,119 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.Collection; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Implementation of properties node walker which helps in forming a new tree from properties node. + * + * @param <T> node child of properties node. + */ +public class DefaultPropertiesNodeWalker<T extends NodeChild> implements PropertiesNodeWalker { + + @Override + public void walk(PropertiesNodeListener listener, + PropertiesNode propertiesNode) throws SvcLogicException { + listener.start(propertiesNode); + walkChildNode(listener, propertiesNode); + listener.end(propertiesNode); + } + + /** + * Walks the children node from the parent node. + * + * @param listener properties node listener + * @param propertiesNode properties node + * @throws SvcLogicException when properties node walking fails + */ + public void walkChildNode(PropertiesNodeListener listener, + PropertiesNode propertiesNode) + throws SvcLogicException { + Map<String, T> children = getChildren(propertiesNode); + if (children != null) { + for (Map.Entry<String, T> entry : children.entrySet()) { + PropertiesNode node = ((PropertiesNode) entry.getValue()); + listener.enterPropertiesNode(node); + walkChildNode(listener, node); + listener.exitPropertiesNode(node); + } + } + if (propertiesNode instanceof RootNode) { + processAugments(propertiesNode, listener); + } + } + + /** + * Processes the augments present in the root node. + * + * @param node root node + * @param listener properties node listener + * @throws SvcLogicException when augment node walking fails + */ + private void processAugments(PropertiesNode node, + PropertiesNodeListener listener) + throws SvcLogicException { + for (Map.Entry<Object, Collection<PropertiesNode>> + augToChild : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> child = augToChild.getValue(); + if (!child.isEmpty()) { + for (PropertiesNode p : child) { + listener.enterPropertiesNode(p); + walkChildNode(listener, p); + listener.exitPropertiesNode(p); + } + } + } + } + + /** + * Returns the children node according to the property node type. + * + * @param value property node + * @return property node children + */ + private Map<String,T> getChildren(PropertiesNode value) { + if (value instanceof RootNode) { + return ((RootNode) value).children(); + } + switch (value.nodeType()) { + case SINGLE_INSTANCE_NODE: + return ((InnerNode) value).children(); + case MULTI_INSTANCE_HOLDER_NODE: + return ((Map<String, T>) ((ListHolderNode) value).children()); + case MULTI_INSTANCE_NODE: + return ((Map<String, T>) ((MultiInstanceNode) value) + .children()); + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + return ((Map<String, T>) ((LeafListHolderNode) value) + .children()); + case SINGLE_INSTANCE_LEAF_NODE: + case MULTI_INSTANCE_LEAF_NODE: + case ANY_XML_NODE: + return null; + default: + throw new IllegalArgumentException("No more types allowed"); + } + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/HolderNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/HolderNode.java new file mode 100644 index 000000000..52bec3412 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/HolderNode.java @@ -0,0 +1,59 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.HashMap; +import java.util.Map; + +/** + * Abstraction of an entity that represents holder node to multi instance node + * in properties data tree. + * + * @param <T> type of child + */ +public abstract class HolderNode<T extends NodeChild> extends PropertiesNode { + + private Map<String, T> children = new HashMap<>(); + + protected HolderNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appInfo, + NodeType nodeType) { + super(name, namespace, uri, parent, appInfo, nodeType); + } + + /** + * Returns children. + * + * @return children + */ + public Map<String, T> children() { + return children; + } + + /** + * Returns child based on index. + * + * @return child based on index + */ + public T child(String index) { + return children.get(index); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/InnerNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/InnerNode.java new file mode 100644 index 000000000..1cf99b2d4 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/InnerNode.java @@ -0,0 +1,215 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.HashMap; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.addToAugmentations; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.createNode; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getAugmentationNode; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getUri; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.isNamespaceAsParent; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.resolveName; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_HOLDER_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_HOLDER_NODE; +import static org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils.findCorrespondingAugment; + +/** + * Abstraction of an entity that represents an inner node to properties data + * tree. + * + * @param <T> type of child + */ +public abstract class InnerNode<T extends NodeChild> extends PropertiesNode { + + private Map<String, T> children = new HashMap<String, T>(); + + protected InnerNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appInfo, NodeType nodeType) { + super(name, namespace, uri, parent, appInfo, nodeType); + } + + /** + * Returns children. + * + * @return children + */ + public Map<String, T> children() { + return children; + } + + /** + * Sets children. + * + * @param children child nodes + */ + public void children(Map<String, T> children) { + this.children = children; + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + PropertiesNode node = ((PropertiesNode) children.get(name)); + if (node != null) { + return node; + } + + // get augment schema, if it is augmented node + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, name); + } + + // create node based on type + if (node == null) { + String uri = getUri(this, name, namespace); + node = createNode(name, namespace, uri, this, appInfo, type); + } + + // If namespace is not same as parent then it is augmented node + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(name, ((T) node)); + } + return node; + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException { + LeafNode node = ((LeafNode) children.get(name)); + if (node != null) { + return node; + } + + AugmentationSchemaNode augSchema = null; + String uri = getUri(this, name, namespace); + node = new LeafNode(name, namespace, uri, this, + appInfo, type, value); + + if (type != NodeType.ANY_XML_NODE) { + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment( + ((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + } + node.valueNs(valueNs); + } + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(name, ((T) node)); + } + return node; + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + Object appInfo) throws SvcLogicException { + String localname = resolveName(name); + PropertiesNode node = ((PropertiesNode) children.get(localname)); + + if (node == null) { + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, localname); + } + + if (node == null) { + String uri = getUri(this, name, namespace); + node = new ListHolderNode(localname, namespace, uri, + this, appInfo, MULTI_INSTANCE_HOLDER_NODE); + } + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(localname, ((T) node)); + } + + node = node.addChild(index, localname, namespace, type, appInfo); + } else if (node instanceof ListHolderNode) { + ListHolderChild child = ((ListHolderNode) node).child(index); + node = (child != null ? ((MultiInstanceNode) child) : + node.addChild(index, localname, namespace, type, appInfo)); + } else { + throw new SvcLogicException("Duplicate node exist with same node"); + } + return node; + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + String value, Namespace valueNs, + Object appInfo) throws SvcLogicException { + String localName = resolveName(name); + PropertiesNode node = ((PropertiesNode) children.get(localName)); + + if (node == null) { + + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, localName); + } + + if (node == null) { + String uri = getUri(this, name, namespace); + node = new LeafListHolderNode(localName, namespace, uri, this, + appInfo, MULTI_INSTANCE_LEAF_HOLDER_NODE); + } + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(localName, ((T) node)); + } + + node = node.addChild(index, localName, namespace, type, value, valueNs, appInfo); + } else if (node instanceof LeafListHolderNode) { + LeafNode child = ((LeafNode) ((HolderNode) node).child(index)); + node = (child != null ? child : node.addChild(index, localName, + namespace, type, + value, valueNs, + appInfo)); + } else { + throw new SvcLogicException("Duplicate node exist with same node"); + } + return node; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderChild.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderChild.java new file mode 100644 index 000000000..e43249a3d --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderChild.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Abstraction of entity representing child's to leaf list holder. + */ +public interface LeafListHolderChild extends NodeChild { + +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderNode.java new file mode 100644 index 000000000..46ab5bb63 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafListHolderNode.java @@ -0,0 +1,93 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Representation of leaf list holder node which will hold multi instance leaf + * node in properties data tree. + */ +public class LeafListHolderNode extends HolderNode<LeafListHolderChild> implements DataNodeChild { + + private static final String node = " holder node"; + + /** + * Creates an instance of LeafListHolderNode. + * + * @param name name of the leaf-list node + * @param namespace namespace of the leaf-list node + * @param uri uri of the leaf-list node + * @param parent parent node of the leaf-list + * @param appInfo application info + * @param nodeType node type + */ + public LeafListHolderNode(String name, Namespace namespace, + String uri, PropertiesNode parent, + Object appInfo, NodeType nodeType) { + super(name, namespace, uri, parent, appInfo, nodeType); + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Leaf cannot be child of leaf-list" + + node); + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Container cannot be child of leaf-list" + + node); + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("List cannot be child of leaf-list" + + node); + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + String value, Namespace valueNs, + Object appInfo) throws SvcLogicException { + LeafNode node = ((LeafNode) children().get(index)); + if (index == null) { + index = String.valueOf(children().size()); + } + String uri = this.uri() + "[" + index + "]"; + node = (node != null) ? node : new LeafNode(name, namespace, uri, + this, appInfo, type, value); + node.valueNs(valueNs); + children().put(index, node); + return node; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafNode.java new file mode 100644 index 000000000..041175ca7 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/LeafNode.java @@ -0,0 +1,122 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Representation of leaf node in properties data tree. + */ +public class LeafNode extends PropertiesNode implements LeafListHolderChild, DataNodeChild { + + private String value; + private Namespace valueNs; + private static final String svcLogicException = "Leaf cannot hold child nodes"; + + /** + * Creates an instance of leaf node. + * + * @param name name of the leaf node + * @param namespace namespace of the leaf node + * @param uri uri of the leaf node + * @param parent parent of the leaf node + * @param appInfo application info + * @param nodeType node type + * @param value value of the leaf + */ + public LeafNode(String name, Namespace namespace, + String uri, PropertiesNode parent, + Object appInfo, NodeType nodeType, + String value) { + super(name, namespace, uri, parent, appInfo, nodeType); + this.value = value; + } + + /** + * Returns value of the leaf. + * + * @return value of the leaf + */ + public String value() { + return value; + } + + /** + * Sets value of the leaf. + * + * @param value value of the leaf + */ + public void value(String value) { + this.value = value; + } + + /** + * Returns value namespace. + * + * @return value namespace + */ + public Namespace valueNs() { + return valueNs; + } + + /** + * Sets value namespace. + * + * @param valueNs value namespace + */ + public void valueNs(Namespace valueNs) { + this.valueNs = valueNs; + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException(svcLogicException); + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException(svcLogicException); + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException(svcLogicException); + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Leaf cannot hold child nodes"); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderChild.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderChild.java new file mode 100644 index 000000000..ec9cc2a0f --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderChild.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Abstraction of entity representing child's to list holder. + */ +public interface ListHolderChild extends NodeChild { + +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderNode.java new file mode 100644 index 000000000..ba9da54cf --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/ListHolderNode.java @@ -0,0 +1,73 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Representation of list holder node which will hold multi instance node in + * properties data tree. + */ +public class ListHolderNode extends HolderNode<ListHolderChild> implements DataNodeChild { + + protected ListHolderNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appInfo, NodeType nodeType) { + super(name, namespace, uri, parent, appInfo, nodeType); + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Leaf cannot be child of list holder node"); + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Container cannot be child of list holder node"); + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + Object appInfo) throws SvcLogicException { + MultiInstanceNode node = ((MultiInstanceNode) children().get(index)); + if (index == null) { + index = String.valueOf(children().size()); + } + String uri = this.uri() + "[" + index + "]"; + node = (node != null) ? node : new MultiInstanceNode(name, namespace, uri, + this, appInfo, type); + children().put(index, node); + return node; + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + String value, Namespace valueNs, + Object appInfo) throws SvcLogicException { + throw new SvcLogicException("Leaf-list cannot be child of list holder node"); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeSerializer.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeSerializer.java new file mode 100644 index 000000000..8a6e75668 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeSerializer.java @@ -0,0 +1,227 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.HashMap; +import java.util.Map; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.DOT_REGEX; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.SLASH; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getChildSchemaNode; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getIndex; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getListName; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getNamespace; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getNodeType; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getParsedValue; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getProcessedPath; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getRevision; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getValueNamespace; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.resolveName; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.ANY_XML_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_LEAF_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.SINGLE_INSTANCE_NODE; + +/** + * Representation of mdsal based properties node serializer implementation. + */ +public class MdsalPropertiesNodeSerializer extends PropertiesNodeSerializer<SchemaNode, SchemaContext> { + + private static final Logger log = LoggerFactory.getLogger( + MdsalPropertiesNodeSerializer.class); + private SchemaNode curSchema; + private PropertiesNode node; + + /** + * Creates the properties node serializer. + * + * @param schemaNode schema node. + * @param schemaCtx schema context + * @param uri URL of the request + */ + public MdsalPropertiesNodeSerializer(SchemaNode schemaNode, + SchemaContext schemaCtx, String uri) { + super(schemaNode, schemaCtx, uri); + } + + @Override + public PropertiesNode encode(Map<String, String> paramMap) throws SvcLogicException { + curSchema = schemaNode(); + String nodeInUri[] = uri().split("\\/"); + String lastNodeName = nodeInUri[nodeInUri.length - 1]; + String rootUri = uri().replaceAll("\\/", "\\."); + node = createRootNode(lastNodeName, rootUri); + + paramMap = convertToValidParam(paramMap); + + updateModNameReq(paramMap, rootUri); + + for (Map.Entry<String, String> entry : paramMap.entrySet()) { + String[] names = entry.getKey().split("\\."); + for (int i = 0; i < names.length; i++) { + if (i < nodeInUri.length) { + if (!(nodeInUri[i].equals(names[i]))) { + break; + } + } else { + createPropertyNode(i, names.length, names[i], + entry.getValue()); + } + } + } + return node; + } + + private void updateModNameReq(Map<String, String> paramMap, + String rootUri) { + String isReqStr = rootUri + "." + "isNonAppend"; + String val = paramMap.get(isReqStr); + if (val != null && val.equals("true")) { + node.nonAppend(true); + } + } + + /** + * Converts all the params in the svc logic context into a valid param by + * replacing the underscore in module name to colon at necessary places. + * + * @param paramMap list of invalid parameters + * @return list of partially valid parameters + */ + private Map<String, String> convertToValidParam(Map<String, String> paramMap) { + Map<String, String> fixedParams = new HashMap<>(); + for(Map.Entry<String, String> entry : paramMap.entrySet()) { + String key = entry.getKey().replaceAll(DOT_REGEX, SLASH); + try { + SchemaPathHolder fixedUrl = getProcessedPath(key, schemaCtx()); + String fixedUri = fixedUrl.getUri().replaceAll( + SLASH, DOT_REGEX); + fixedParams.put(fixedUri, entry.getValue()); + } catch (IllegalArgumentException | RestconfDocumentedException + | NullPointerException e) { + log.info("Exception while processing properties by replacing " + + "underscore with colon. Process the properties as it is." + e); + fixedParams.put(entry.getKey(), entry.getValue()); + } + } + return fixedParams; + } + + @Override + public Map<String, String> decode(PropertiesNode propertiesNode) + throws SvcLogicException { + PropertiesNodeWalker walker = new DefaultPropertiesNodeWalker<>(); + DefaultPropertiesNodeListener listener = new DefaultPropertiesNodeListener(); + walker.walk(listener, propertiesNode); + return listener.params(); + } + + private RootNode createRootNode(String lastNodeName, String rootUri) { + Module m = SchemaContextUtil.findParentModule(schemaCtx(), curSchema); + Namespace ns = new Namespace(m.getName(), m.getNamespace(), + getRevision(m.getRevision())); + return new RootNode(lastNodeName, ns, schemaNode(), rootUri); + } + + private void createPropertyNode(int index, int length, String name, + String value) throws SvcLogicException { + + Namespace ns = getNamespace(getListName(name), schemaCtx(), + node, curSchema); + String localName = resolveName(ns, name); + SchemaNode schema = getChildSchemaNode(curSchema, localName, ns); + if (schema == null) { + return; + } + + switch (getNodeType(index, length, name, schema)) { + case SINGLE_INSTANCE_NODE: + node = node.addChild(localName, ns, + SINGLE_INSTANCE_NODE, schema); + curSchema = schema; + break; + + case MULTI_INSTANCE_NODE: + node = node.addChild(getIndex(name), localName, ns, + MULTI_INSTANCE_NODE, schema); + curSchema = schema; + break; + + case SINGLE_INSTANCE_LEAF_NODE: + addLeafNode(value, SINGLE_INSTANCE_LEAF_NODE, localName, + ns, schema, name); + break; + + case MULTI_INSTANCE_LEAF_NODE: + addLeafNode(value, MULTI_INSTANCE_LEAF_NODE, localName, + ns, schema, name); + break; + + case ANY_XML_NODE: + node = node.addChild(localName, ns, ANY_XML_NODE, + value, null, schema); + node = node.endNode(); + curSchema = ((SchemaNode) node.appInfo()); + break; + + default: + throw new SvcLogicException("Invalid node type"); + } + } + + /** + * Adds leaf property node to the current node. + * + * @param value value of the leaf node + * @param type single instance or multi instance leaf node + * @param localName name of the leaf node + * @param ns namespace of the leaf node + * @param schema schema of the leaf node + * @param name name of the leaf in properties + * @throws SvcLogicException exception while adding leaf node + */ + private void addLeafNode(String value, NodeType type, + String localName, Namespace ns, + SchemaNode schema, String name) throws SvcLogicException { + Namespace valNs = getValueNamespace(value, schemaCtx()); + value = getParsedValue(valNs, value); + if (SINGLE_INSTANCE_LEAF_NODE == type) { + node = node.addChild(localName, ns, SINGLE_INSTANCE_LEAF_NODE, + value, valNs, schema); + } else { + node = node.addChild(getIndex(name), localName, ns, + MULTI_INSTANCE_LEAF_NODE, value, + valNs, schema); + } + node = node.endNode(); + curSchema = ((SchemaNode) node.appInfo()); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java new file mode 100644 index 000000000..e6fa064f5 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MdsalPropertiesNodeUtils.java @@ -0,0 +1,578 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; +import java.util.Optional; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; +import org.opendaylight.yangtools.yang.data.util.ParserStreamUtils; +import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; +import static java.util.regex.Pattern.quote; +import static org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier.toInstanceIdentifier; + +/** + * Represents utilities for properties node tree. + */ +public final class MdsalPropertiesNodeUtils { + + static final String COLON = ":"; + + static final String UNDERSCORE = "_"; + + static final String SLASH = "/"; + + static final String DOT_REGEX = "\\."; + + private static final String INFO_MSG = "The %s formed is currently not" + + " valid"; + + private static final String EXC_MSG = "Unable to form a formatted path"; + + /** + * Logger for the Mdsal properties util class. + */ + private static final Logger log = LoggerFactory.getLogger( + MdsalPropertiesNodeUtils.class); + + private MdsalPropertiesNodeUtils() { + } + + /** + * Returns the index from multi instance property name. + * + * @param name name of the property + * @return index from multi instance property name + */ + public static String getIndex(String name) { + return name.substring(name.indexOf("[") + 1, + name.indexOf("]")); + } + + /** + * Returns the multi instance property name. + * + * @param name name of the property + * @return the multi instance property name + */ + public static String getListName(String name) { + String[] s = name.split("\\["); + return s[0]; + } + + /** + * Returns true if property is multi instance. + * + * @param name name of the property + * @return true if property is multi instance + */ + public static boolean isListEntry(String name) { + String s[] = name.split("\\["); + return s.length > 1; + } + + /** + * Returns name of the property after pruning namespace and + * index if the property is multi instance. + * + * @param name name of the property + * @return name of the property + */ + static String resolveName(String name) { + String localName = getListName(name); + final int lastIndexOfColon = localName.lastIndexOf(":"); + if (lastIndexOfColon != -1) { + localName = localName.substring(lastIndexOfColon + 1); + } + return localName; + } + + /** + * Returns name of the property after pruning namespace and index if the + * property is multi instance by knowing the module name from namespace. + * + * @param ns namespace + * @param name name of the node + * @return resolved name + */ + static String resolveName(Namespace ns, String name) { + String localName = getListName(name); + String modName = ns.moduleName(); + if ((localName.contains(COLON) || localName.contains(UNDERSCORE)) + && localName.startsWith(modName)) { + localName = localName.substring(modName.length()+1); + } + return localName; + } + + /** + * Adds current node to parent's augmentation map. + * + * @param augSchema augment schema + * @param parent parent property node + * @param curNode current property node + */ + public static void addToAugmentations(AugmentationSchemaNode augSchema, + PropertiesNode parent, + PropertiesNode curNode) { + Collection<PropertiesNode> childsFromAugmentation = parent + .augmentations().get(augSchema); + if (!childsFromAugmentation.isEmpty()) { + for (PropertiesNode pNode : childsFromAugmentation) { + if (pNode.name().equals(curNode.name())) { + return; + } + } + } + parent.augmentations().put(augSchema, curNode); + } + + + /** + * Returns augmented properties node if it is already + * added in properties tree. + * + * @param augSchema augmented schema node + * @param parent parent properties node + * @param name name of the properties + * @return augmented properties node if it is already added + */ + public static PropertiesNode getAugmentationNode( + AugmentationSchemaNode augSchema, + PropertiesNode parent, String name) { + if (augSchema == null) { + return null; + } + + Collection<PropertiesNode> childsFromAugmentation = parent + .augmentations().get(augSchema); + if (!childsFromAugmentation.isEmpty()) { + for (PropertiesNode pNode : childsFromAugmentation) { + if (pNode.name().equals(name)) { + return pNode; + } + } + } + + return null; + } + + /** + * Creates uri with specified name and namespace. + * + * @param parent parent properties node + * @param name name of the node + * @param ns namespace of the node + * @return uri with specified name and namespace + */ + public static String getUri(PropertiesNode parent, String name, + Namespace ns) { + String uri = name; + if (!(parent.namespace().moduleNs().equals(ns.moduleNs()))) { + uri = ns.moduleName() + ":" + name; + } + return parent.uri() + "." + uri; + } + + /** + * Creates new properties with specified parameters. + * + * @param name name of the properties node + * @param namespace namespace of the properties node + * @param uri uri of the properties node + * @param parent parent node + * @param appInfo application info + * @param type node type + * @return new properties node + * @throws SvcLogicException exception while creating properties node + */ + public static PropertiesNode createNode(String name, Namespace namespace, + String uri, PropertiesNode parent, + Object appInfo, NodeType type) + throws SvcLogicException { + switch (type) { + case SINGLE_INSTANCE_NODE: + return new SingleInstanceNode(name, namespace, uri, parent, appInfo, type); + case MULTI_INSTANCE_HOLDER_NODE: + return new ListHolderNode(name, namespace, uri, parent, appInfo, type); + case MULTI_INSTANCE_LEAF_HOLDER_NODE: + return new LeafListHolderNode(name, namespace, uri, parent, appInfo, type); + default: + throw new SvcLogicException("Invalid node type " + type); + } + } + + /** + * Returns true if namespace is same as parent's namespace. + * + * @param parent parent property node + * @param curNode current property node + * @return true if namespace is same as parent namespace + */ + public static boolean isNamespaceAsParent(PropertiesNode parent, + PropertiesNode curNode) { + return parent.namespace().moduleNs().equals(curNode.namespace().moduleNs()); + } + + /** + * Returns the schema path holder with a formatted url and the instance + * identifier context from a given uri or the parameters from svc logic + * context. + * + * @param uri unformatted uri or parameter + * @param context schema context + * @return schema path holder + */ + public static SchemaPathHolder getProcessedPath(String uri, + SchemaContext context) { + + String uri1 = uri.replaceAll(UNDERSCORE, COLON); + try { + InstanceIdentifierContext<?> id = toInstanceIdentifier( + uri1, context, null); + return new SchemaPathHolder(id, uri1); + } catch (IllegalArgumentException | RestconfDocumentedException + | NullPointerException e) { + log.info("Exception while converting uri to instance identifier" + + " context. Process each node in uri to get instance identifier" + + " context " + e); + return processNodesAndAppendPath(uri, context); + } + } + + /** + * Processes the nodes in the given uri and finds instance identifier + * context till it reaches the last node in uri. If its not able to find + * schema for the path, it appends the suffix part and puts it back in + * the param list. + * + * @param uri uri with underscore + * @param context schema context + * @return schema and path holder + */ + private static SchemaPathHolder processNodesAndAppendPath(String uri, + SchemaContext context) { + + String actPath = ""; + SchemaPathHolder id = new SchemaPathHolder(null, ""); + String[] uriParts = uri.split(SLASH); + String sec = ""; + if (uri.contains(UNDERSCORE)) { + sec = uri.substring(uriParts[0].length()+1); + } + for (int i = 0; i<uriParts.length; i++) { + + try { + id = processIdentifier(uriParts[i], context, actPath); + } catch (IllegalArgumentException e) { + log.info(format(EXC_MSG, e)); + id.setUri(actPath+ uriParts[i] + sec); + return id; + } + + actPath = actPath + id.getUri() + SLASH; + if (sec.startsWith(SLASH)) { + sec = sec.replaceFirst(SLASH, ""); + } + if (i+1 < uriParts.length) { + sec = sec.replaceFirst(quote(uriParts[i + 1]), ""); + } + } + id.setUri(actPath.substring(0,actPath.length() - 1)); + return id; + } + + /** + * Processes the schema and path holder for a given node in the path. It + * figures if the path is valid by replacing underscore in the node + * consecutively, till it finds the proper schema for the node. + * + * @param node node in the path + * @param context schema context + * @param prefix prefix for the node in the path + * @return schema and path holder + */ + private static SchemaPathHolder processIdentifier(String node, + SchemaContext context, + String prefix) { + + String[] values = node.split(UNDERSCORE); + String val = values[0]; + StringBuilder firstHalf = new StringBuilder(); + String secondHalf = ""; + if (node.contains(UNDERSCORE)) { + secondHalf = node.substring(values[0].length()+1); + } + InstanceIdentifierContext<?> id; + for (int i = 0; i< values.length-1; i++) { + val = values[i]; + val = firstHalf + val + COLON + secondHalf; + try { + id = toInstanceIdentifier(prefix + val, context, null); + return new SchemaPathHolder(id, val); + } catch (IllegalArgumentException | RestconfDocumentedException | + NullPointerException e) { + log.info(format(INFO_MSG, val, e)); + } + firstHalf.append(values[i]).append(UNDERSCORE); + secondHalf = secondHalf.replaceFirst( + values[i + 1] + UNDERSCORE,""); + } + val = val.replace(COLON,UNDERSCORE); + try { + id = toInstanceIdentifier(prefix + val, context, null); + return new SchemaPathHolder(id, val); + } catch (IllegalArgumentException | RestconfDocumentedException | + NullPointerException e1) { + throw new IllegalArgumentException(EXC_MSG, e1); + } + } + + /** + * Returns the namespace of the given node name. If the node name is + * separated by colon, the it splits with colon and forms the namespace. + * If the node name is formed with underscore, then it splits the node + * name consecutively to figure out the proper module name. + * + * @param childName node name + * @param ctx schema context + * @param parent parent properties node + * @param curSchema current schema + * @return namespace of the given node + */ + static Namespace getNamespace(String childName, SchemaContext ctx, + PropertiesNode parent, SchemaNode curSchema) { + + Namespace parentNs = parent.namespace(); + Namespace ns = new Namespace(parentNs.moduleName(), + parentNs.moduleNs(), parentNs.revision()); + int lastIndexOfColon = childName.lastIndexOf(COLON); + if (lastIndexOfColon != -1) { + String moduleName = childName.substring(0, lastIndexOfColon); + childName = childName.substring(lastIndexOfColon+1); + Namespace ns1 = getNs(moduleName, ctx); + if (ns1 != null) { + ns = ns1; + } + } + + SchemaNode child = getChildSchemaNode(curSchema, childName, ns); + + if (child == null && childName.contains(UNDERSCORE)) { + String[] children = childName.split(UNDERSCORE); + String second = childName.substring(children[0].length() + 1); + StringBuilder first = new StringBuilder(); + + for (int i =0; i< children.length; i++) { + String moduleName = first + children[i]; + Namespace newNs = getNs(moduleName, ctx); + if (newNs != null) { + return newNs; + } + first.append(children[i]).append(UNDERSCORE); + if (i + 1 < children.length) { + second = second.replaceFirst( + children[i + 1] + UNDERSCORE, ""); + } + } + return ns; + } + return ns; + } + + /** + * Returns the namespace by finding the given module in the schema context. + * + * @param modName module name + * @param ctx schema context + * @return namespace of the given node name + */ + private static Namespace getNs(String modName, SchemaContext ctx) { + Iterator<Module> it = ctx.findModules(modName).iterator(); + if (it.hasNext()) { + Module m = it.next(); + return new Namespace(modName, m.getQNameModule().getNamespace(), + getRevision(m.getRevision())); + } + return null; + } + + /** + * Returns child schema node. + * + * @param curSchema current schema node + * @param name name of the property + * @param namespace namespace of the property + * @return child schema node + */ + public static SchemaNode getChildSchemaNode(SchemaNode curSchema, + String name, + Namespace namespace) { + if (namespace == null) { + return null; + } + + QName qname = QName.create(namespace.moduleNs(), + Revision.of(namespace.revision()), name); + + // YANG RPC will not be instance of DataSchemaNode + if (curSchema instanceof DataSchemaNode) { + Deque<DataSchemaNode> schemaNodeDeque = ParserStreamUtils. + findSchemaNodeByNameAndNamespace(((DataSchemaNode) + curSchema), name, namespace.moduleNs()); + if (schemaNodeDeque.isEmpty()) { + // could not find schema node + return null; + } + + DataSchemaNode schemaNode = schemaNodeDeque.pop(); + if (schemaNodeDeque.isEmpty()){ + // Simple node + return schemaNode; + } + + // node is child of Choice/case + return SchemaUtils.findSchemaForChild(((ChoiceSchemaNode) schemaNode), + qname); + } else { + return SchemaUtils.findDataChildSchemaByQName(curSchema, qname); + } + } + + /** + * Returns the property node type. + * + * @param index current index + * @param length length of the properties + * @param name name of the property + * @return the property node type + */ + public static NodeType getNodeType(int index, int length, String name, + SchemaNode schema) { + if (index == length-1) { + if (schema instanceof AnyXmlSchemaNode){ + return NodeType.ANY_XML_NODE; + } + return (isListEntry(name) ? NodeType.MULTI_INSTANCE_LEAF_NODE : + NodeType.SINGLE_INSTANCE_LEAF_NODE); + } else { + return (isListEntry(name) ? NodeType.MULTI_INSTANCE_NODE : + NodeType.SINGLE_INSTANCE_NODE); + } + } + + /** + * Returns revision in string. + * + * @param r YANG revision + * @return revision in string + */ + public static String getRevision(Optional<Revision> r) { + return (r.isPresent()) ? r.get().toString() : null; + } + + /** + * Returns value namespace for leaf value. + * + * @param value value of the leaf + * @param ctx schema context + * @return value namespace + * @throws SvcLogicException if identity/module could not be found + */ + static Namespace getValueNamespace(String value, + SchemaContext ctx) + throws SvcLogicException { + String prefix = getPrefixFromValue(value); + if (prefix == null) { + return null; + } + + IdentitySchemaNode id = IdentityCodecUtil.parseIdentity(value, + ctx, + prefixToModule -> { + final Iterator<Module> modules = ctx.findModules(prefix).iterator(); + checkArgument(modules.hasNext(), "Could not find " + + "module %s", prefix); + return modules.next().getQNameModule(); + }); + + if (id == null) { + throw new SvcLogicException("Could not find identity"); + } + + return getModuleNamespace(id.getQName(), ctx); + } + + private static String getPrefixFromValue(String value) { + int lastIndexOfColon = value.lastIndexOf(":"); + if (lastIndexOfColon != -1) { + return value.substring(0, lastIndexOfColon); + } + return null; + } + + /** + * Returns module namespace from a given qName. + * + * @param qName qName of a node + * @param ctx schema context + * @return module namespace of the node + * @throws SvcLogicException when the module is not available + */ + public static Namespace getModuleNamespace(QName qName, SchemaContext ctx) + throws SvcLogicException { + Optional<Module> module = ctx.findModule(qName.getModule()); + if (!module.isPresent()) { + throw new SvcLogicException("Could not find module node"); + } + Module m = module.get(); + return new Namespace(m.getName(), m.getQNameModule().getNamespace(), + getRevision(m.getRevision())); + } + + static String getParsedValue(Namespace valNs, String value) { + if (valNs != null && value.contains(":")) { + String[] valArr = value.split(":"); + return valArr[1]; + } + return value; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MultiInstanceNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MultiInstanceNode.java new file mode 100644 index 000000000..c251cdaea --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/MultiInstanceNode.java @@ -0,0 +1,32 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Representation of multi instance node in properties data tree. + */ +public class MultiInstanceNode extends InnerNode<DataNodeChild> implements ListHolderChild { + + public MultiInstanceNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appinfo, NodeType nodeType) { + super(name, namespace, uri, parent, appinfo, nodeType); + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/Namespace.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/Namespace.java new file mode 100644 index 000000000..4f27ee1e7 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/Namespace.java @@ -0,0 +1,100 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.net.URI; + +/** + * Representation of YANG namespace. + */ +public class Namespace { + private String moduleName; + private URI moduleNs; + private String revision; + + /** + * Creates an instance of namespace with specified module name, + * namespace and revision. + * + * @param modulename module name + * @param moduleNs module namespace + * @param revision revision + */ + public Namespace(String modulename, URI moduleNs, String revision) { + this.moduleName = modulename; + this.moduleNs = moduleNs; + this.revision = revision; + } + + /** + * Returns module name. + * + * @return module name + */ + public String moduleName() { + return moduleName; + } + + /** + * Sets module name. + * + * @param moduleName module name + */ + public void moduleName(String moduleName) { + this.moduleName = moduleName; + } + + /** + * Sets module namespace. + * + * @return module namespace + */ + public URI moduleNs() { + return moduleNs; + } + + /** + * Sets module namespace. + * + * @param moduleNs module namespace + */ + public void moduleNs(URI moduleNs) { + this.moduleNs = moduleNs; + } + + /** + * Returns revision. + * + * @return revision + */ + public String revision() { + return revision; + } + + /** + * Sets revision. + * + * @param revision revision + */ + public void revision(String revision) { + this.revision = revision; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeChild.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeChild.java new file mode 100644 index 000000000..d482c6763 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeChild.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Abstraction of an entity that represents child's of a node. + */ +public interface NodeChild { + +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeType.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeType.java new file mode 100644 index 000000000..0b6974e3e --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/NodeType.java @@ -0,0 +1,34 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Representation of types of node in properties node tree. + */ +public enum NodeType { + SINGLE_INSTANCE_NODE, + MULTI_INSTANCE_NODE, + SINGLE_INSTANCE_LEAF_NODE, + MULTI_INSTANCE_LEAF_NODE, + MULTI_INSTANCE_HOLDER_NODE, + MULTI_INSTANCE_LEAF_HOLDER_NODE, + ANY_XML_NODE +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNode.java new file mode 100644 index 000000000..7e0ad631e --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNode.java @@ -0,0 +1,280 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of properties node data tree. This intermediate representation + * will enable data format serializers to be agnostic of DG context memory + * nuances and thereby will enable faster development of new data format + * serializers. + */ +public abstract class PropertiesNode { + + private String name; + private Namespace namespace; + private String uri; + private PropertiesNode parent; + private Object appInfo; + private NodeType nodeType; + private boolean nonAppend; + private Multimap<Object, PropertiesNode> augmentations = ArrayListMultimap.create(); + + /** + * Creates an instance of properties node. + * + * @param name name of node + * @param namespace namespace of node, null indicates parent namespace + * @param uri URI of this node, if null its calculated based on parent and + * current value of name and namespace + * @param parent parent's node + * @param appInfo application related information + * @param nodeType node type + */ + protected PropertiesNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appInfo, NodeType nodeType) { + this.name = name; + this.namespace = namespace; + this.uri = uri; + this.parent = parent; + this.appInfo = appInfo; + this.nodeType = nodeType; + } + + /** + * Sets name. + * + * @param name name of the node + */ + public void name(String name) { + this.name = name; + } + + /** + * Sets namespace. + * + * @param namespace namespace of the node + */ + public void namespace(Namespace namespace) { + this.namespace = namespace; + } + + /** + * Sets uri. + * + * @param uri uri of the node + */ + public void uri(String uri) { + this.uri = uri; + } + + /** + * Sets parent node. + * + * @param parent parent node + */ + public void parent(PropertiesNode parent) { + this.parent = parent; + } + + /** + * Sets application info. + * + * @param appInfo application info + */ + public void appInfo(Object appInfo) { + this.appInfo = appInfo; + } + + /** + * Sets to true if module name is required in forming a request; false + * otherwise. + * + * @param isNotReq true if required; false otherwise + */ + public void nonAppend(boolean isNotReq) { + this.nonAppend = isNotReq; + } + + /** + * Returns parent. + * + * @return parent node + */ + public PropertiesNode parent() { + return parent; + } + + /** + * Returns name. + * + * @return name of the node + */ + public String name() { + return name; + } + + /** + * Returns namespace. + * + * @return namespace of the node + */ + public Namespace namespace() { + return namespace; + } + + /** + * Returns uri. + * + * @return uri of the node + */ + public String uri() { + return uri; + } + + /** + * Returns application info. + * + * @return application info + */ + public Object appInfo() { + return appInfo; + } + + /** + * Returns node type. + * + * @return node type + */ + public NodeType nodeType() { + return nodeType; + } + + /** + * Returns if module name is required. + * + * @return status of module name if required + */ + public boolean nonAppend() { + return nonAppend; + } + + /** + * Returns augmentations. + * + * @return augmentations + */ + public Multimap<Object, PropertiesNode> augmentations() { + return augmentations; + } + + /** + * Sets augmentations. + * + * @param augmentations augmentations of the node + */ + public void augmentations(Multimap<Object, PropertiesNode> augmentations) { + this.augmentations = augmentations; + } + + /** + * Adds a child to a current node. + * + * @param name name of child + * @param namespace namespace of child, null represents parent namespace + * @param type type of node + * @param appInfo application info + * @return added properties node + */ + public abstract PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException; + + /** + * Adds a child with value to a current node. + * + * @param name name of child + * @param namespace namespace of child, null represents parent namespace + * @param type type of node + * @param value value of node + * @param valueNs value namespace + * @param appInfo application info + * @throws SvcLogicException if failed to add child + * @return added properties node + */ + public abstract PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valueNs, + Object appInfo) throws SvcLogicException; + + /** + * Adds a child at a given index to a current node. To be used in case of + * leaf holder child's which is multi instance node. + * + * @param index index at which node is to be added + * @param name name of child + * @param namespace namespace of child, null represents parent namespace + * @param type type of node + * @param appInfo application info + * @throws SvcLogicException if failed to add child + * @return added properties node + */ + public abstract PropertiesNode addChild(String index, String name, + Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException; + + /** + * Adds a child at a given index to a current node. To be used in case of + * leaf holder child's which is multi instance node. + * + * @param index index at which node is to be added + * @param name name of child + * @param namespace namespace of child, null represents parent namespace + * @param type type of node + * @param value value of node + * @param valueNs value namespace + * @param appInfo application info + * @throws SvcLogicException if failed to add child + * @return added properties node + */ + public abstract PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + String value, Namespace valueNs, + Object appInfo) throws SvcLogicException; + + /** + * Returns root node. + * + * @return root node + */ + public PropertiesNode endNode() { + PropertiesNode node = this; + while (node.parent() != null){ + node = node.parent(); + } + return node; + } +} + diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeListener.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeListener.java new file mode 100644 index 000000000..7b97ffaf8 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeListener.java @@ -0,0 +1,61 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of properties node listener. + */ +public interface PropertiesNodeListener { + + /** + * Pre-configurations required before starting the walking. + * + * @param node properties node + * @throws SvcLogicException when the pre-configuration fails + */ + void start(PropertiesNode node) throws SvcLogicException; + + /** + * Post-configurations required after starting the walking. + * + * @param node properties node + * @throws SvcLogicException when the post-configuration fails + */ + void end(PropertiesNode node) throws SvcLogicException; + + /** + * Enters the properties node. + * + * @param node properties node + * @throws SvcLogicException when entering the properties node fails + */ + void enterPropertiesNode(PropertiesNode node) throws SvcLogicException; + + /** + * Enters the properties node. + * + * @param node properties node + * @throws SvcLogicException when exiting the properties node fails + */ + void exitPropertiesNode(PropertiesNode node) throws SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeSerializer.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeSerializer.java new file mode 100644 index 000000000..fe6fed484 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeSerializer.java @@ -0,0 +1,112 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +import java.util.Map; + +/** + * Abstraction of an entity to enable encoding and decoding of properties + * to an abstract properties node tree using YANG based schema. + * This serializer will be used by other data format serializers and will keep + * them abstract from properties nuances thereby enabling quick addition of any + * new data format serializer. + * + * @param <T> type of schema node + * @param <P> schema context of the model + */ +public abstract class PropertiesNodeSerializer<T, P> { + + /** + * Schema node from which the property is made. + */ + private T schemaNode; + + /** + * Schema context of the model. + */ + private P schemaCtx; + + /** + * URL pointing to the schema node. + */ + private String uri; + + /** + * Creates the properties node serializer. + * + * @param schemaNode schema node. + * @param schemaCtx schema context + * @param uri URL of the request + */ + public PropertiesNodeSerializer(T schemaNode, P schemaCtx, String uri) { + this.schemaNode = schemaNode; + this.schemaCtx = schemaCtx; + this.uri = uri; + } + + /** + * Encodes from properties to properties-node tree. + * + * @param paramMap parameter map + * @throws SvcLogicException fails to encode properties to properties node + * @return properties node + */ + public abstract PropertiesNode encode(Map<String, String> paramMap) throws SvcLogicException; + + /** + * Decodes from properties-node to properties map. + * + * @param propertiesNode properties-node + * @return parameter map + * @throws SvcLogicException fails to decode properties node to properties + */ + public abstract Map<String, String> decode(PropertiesNode propertiesNode) + throws SvcLogicException; + + /** + * Returns the schema node of the property + * + * @return schema node + */ + public T schemaNode(){ + return schemaNode; + } + + /** + * Returns the schema context + * + * @return schema node + */ + public P schemaCtx() { + return schemaCtx; + } + + /** + * Returns the URI. + * + * @return uri + */ + public String uri() { + return uri; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeWalker.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeWalker.java new file mode 100644 index 000000000..36ee4dd9d --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesNodeWalker.java @@ -0,0 +1,38 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +/** + * Abstraction of properties node walker + */ +public interface PropertiesNodeWalker { + /** + * Walks the properties node with the listener. + * + * @param listener properties node listener. + * @param propertiesNode properties node + * @throws SvcLogicException when walking the properties node fails + */ + void walk(PropertiesNodeListener listener, PropertiesNode propertiesNode) + throws SvcLogicException; +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/RootNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/RootNode.java new file mode 100644 index 000000000..c7f7340fd --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/RootNode.java @@ -0,0 +1,212 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; + +import java.util.HashMap; +import java.util.Map; + +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.addToAugmentations; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.createNode; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getAugmentationNode; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.getUri; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.isNamespaceAsParent; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.MdsalPropertiesNodeUtils.resolveName; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_HOLDER_NODE; +import static org.onap.ccsdk.sli.plugins.yangserializers.pnserializer.NodeType.MULTI_INSTANCE_LEAF_HOLDER_NODE; +import static org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils.findCorrespondingAugment; + +/** + * Abstraction of node representing properties data tree. + */ +public class RootNode<T extends NodeChild> extends PropertiesNode { + + private Map<String, T> children = new HashMap<String, T>(); + + /** + * Creates an instance of the root node to build the properties. + * + * @param name name of the node + * @param namespace namespace of the node + * @param appInfo application info + * @param uri URI of the node + */ + public RootNode(String name, Namespace namespace, + Object appInfo, String uri) { + super(name, namespace, uri, null, appInfo, null); + } + + /** + * Returns children. + * + * @return children + */ + public Map<String, T> children() { + return children; + } + + /** + * Sets children. + * + * @param children child nodes + */ + public void children(Map<String, T> children) { + this.children = children; + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, + Object appInfo) throws SvcLogicException { + PropertiesNode node = ((PropertiesNode) children.get(name)); + if (node != null) { + return node; + } + + // get augment schema, if it is augmented node + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, name); + } + + // create node based on type, this api will be invoked only for these three types + if (node == null) { + String uri = getUri(this, name, namespace); + node = createNode(name, namespace, uri, this, appInfo, type); + } + + // If namespace is not same as parent then it is augmented node + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(name, ((T) node)); + } + return node; + } + + @Override + public PropertiesNode addChild(String name, Namespace namespace, + NodeType type, String value, + Namespace valuens, + Object appInfo) throws SvcLogicException { + LeafNode node = ((LeafNode) children.get(name)); + if (node != null) { + return node; + } + + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + } + + String uri = getUri(this, name, namespace); + node = new LeafNode(name, namespace, uri, this, + appInfo, type, value); + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(name, ((T) node)); + } + return node; + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + Object appInfo) throws SvcLogicException { + String localname = resolveName(name); + PropertiesNode node = ((PropertiesNode) children.get(localname)); + if (node == null) { + String uri = getUri(this, name, namespace); + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, localname); + } + + if (node == null) { + node = new ListHolderNode(localname, namespace, uri, + this, appInfo, MULTI_INSTANCE_HOLDER_NODE); + } + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(localname, ((T) node)); + } + node = node.addChild(index, localname, namespace, type, appInfo); + } else if (node instanceof ListHolderNode) { + ListHolderChild child = ((ListHolderNode) node).child(index); + node = (child != null ? ((MultiInstanceNode) child) : + node.addChild(index, localname, namespace, type, appInfo)); + } else { + throw new SvcLogicException("Duplicate node exist with same node"); + } + return node; + } + + @Override + public PropertiesNode addChild(String index, String name, + Namespace namespace, NodeType type, + String value, Namespace valueNs, + Object appInfo) throws SvcLogicException { + String localName = resolveName(name); + PropertiesNode node = ((PropertiesNode) children.get(localName)); + if (node == null) { + String uri = getUri(this, name, namespace); + AugmentationSchemaNode augSchema = null; + if (((DataSchemaNode) appInfo).isAugmenting()) { + augSchema = findCorrespondingAugment(((DataSchemaNode) this.appInfo()), + ((DataSchemaNode) appInfo)); + node = getAugmentationNode(augSchema, this, localName); + } + + if (node == null) { + node = new LeafListHolderNode(localName, namespace, uri, this, + appInfo, MULTI_INSTANCE_LEAF_HOLDER_NODE); + } + + if (augSchema != null && !isNamespaceAsParent(this, node)) { + addToAugmentations(augSchema, this, node); + } else { + children.put(localName, ((T) node)); + } + node = node.addChild(index, localName, namespace, type, value, valueNs, appInfo); + } else if (node instanceof LeafListHolderNode) { + LeafNode child = ((LeafNode) ((HolderNode) node).child(index)); + node = (child != null ? child : node.addChild(index, localName, + namespace, type, + value, valueNs, + appInfo)); + } else { + throw new SvcLogicException("Duplicate node exist with same node"); + } + return node; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SchemaPathHolder.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SchemaPathHolder.java new file mode 100644 index 000000000..4ef1bc664 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SchemaPathHolder.java @@ -0,0 +1,77 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; + +/** + * Representation of a holder for a proper path and its corresponding schema. + */ +public class SchemaPathHolder { + + /** + * Schema context for the path. + */ + private InstanceIdentifierContext insId; + + /** + * Formatted path. + */ + private String uri; + + /** + * Constructs schema path holder with path and its schema. + * + * @param insId instance identifier context + * @param uri path + */ + public SchemaPathHolder(InstanceIdentifierContext insId, String uri) { + this.insId = insId; + this.uri = uri; + } + + /** + * Returns the instance identifier context of the path. + * + * @return schema of the path + */ + public InstanceIdentifierContext getInsId() { + return insId; + } + + /** + * Returns the formatted path. + * + * @return formatted path + */ + public String getUri() { + return uri; + } + + /** + * Sets the formatted path. + * + * @param uri formatted path + */ + public void setUri(String uri) { + this.uri = uri; + } +} diff --git a/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SingleInstanceNode.java b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SingleInstanceNode.java new file mode 100644 index 000000000..6eb24c1a4 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/SingleInstanceNode.java @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +/** + * Representation of single instance node in properties data tree. + */ +public class SingleInstanceNode extends InnerNode<DataNodeChild> implements DataNodeChild { + + public SingleInstanceNode(String name, Namespace namespace, String uri, + PropertiesNode parent, Object appInfo, + NodeType nodeType) { + super(name, namespace, uri, parent, appInfo, nodeType); + } +} diff --git a/plugins/restconf-client/provider/src/main/resources/OSGI-INF/blueprint/restconf-client-blueprint.xml b/plugins/restconf-client/provider/src/main/resources/OSGI-INF/blueprint/restconf-client-blueprint.xml new file mode 100755 index 000000000..45c335b13 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/resources/OSGI-INF/blueprint/restconf-client-blueprint.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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========================================================= + --> + +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <reference xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0" + id="restapiCallNodeProvider" + interface="org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode" + ext:proxy-method="classes"/> + + <bean id="restconfapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode" > + <argument ref="restapiCallNodeProvider"/> + </bean> + + <bean id="restconfDiscoveryNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode" > + <argument ref="restconfapiCallNodeProvider"/> + </bean> + + <service ref="restconfapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode</value> + </interfaces> + </service> + + <service ref="restconfDiscoveryNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml b/plugins/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml new file mode 100755 index 000000000..45c335b13 --- /dev/null +++ b/plugins/restconf-client/provider/src/main/resources/org/opendaylight/blueprint/restconf-client-blueprint.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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========================================================= + --> + +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <reference xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0" + id="restapiCallNodeProvider" + interface="org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode" + ext:proxy-method="classes"/> + + <bean id="restconfapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode" > + <argument ref="restapiCallNodeProvider"/> + </bean> + + <bean id="restconfDiscoveryNodeProvider" class="org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode" > + <argument ref="restconfapiCallNodeProvider"/> + </bean> + + <service ref="restconfapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode</value> + </interfaces> + </service> + + <service ref="restconfDiscoveryNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.restconfdiscovery.RestconfDiscoveryNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java new file mode 100644 index 000000000..35ac221fb --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/SseServerMock.java @@ -0,0 +1,68 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.glassfish.jersey.media.sse.EventOutput; +import org.glassfish.jersey.media.sse.OutboundEvent; +import org.glassfish.jersey.media.sse.SseFeature; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.io.IOException; + +@Path("events") +public class SseServerMock { + + @GET + @Produces(SseFeature.SERVER_SENT_EVENTS) + public EventOutput getServerSentEvents() throws IOException { + String data = "{" + + "\"ietf-restconf:notification\" : {" + + " \"eventTime\" : \"2017-10-25T08:22:33.44Z\"," + + " \"ietf-yang-push:push-change-update\": {" + + "\"subscription-id\":\"89\"," + + "\"datastore-changes\": {" + + "\"ietf-yang-patch:yang-patch\":{" + + "\"patch-id\":\"1\"," + + "\"edit\":[{" + + "\"edit-id\":\"edit1\"," + + "\"operation\":\"merge\"," + + "\"target\":\"/ietf-interfaces:interfaces-state\"," + + "\"value\": {" + + "\"ietf-interfaces:interfaces-state\":{"+ + "\"interface\": {" + + "\"name\":\"eth0\"," + + "\"oper-status\":\"down\"," + + "}" + + "}" + + "}" + + "}]"+ + "}" + + "}" + + "}" + + "}" + + "}"; + final EventOutput result = new EventOutput(); + result.write(new OutboundEvent.Builder().data(String.class, data).build()); + result.close(); + return result; + } +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java new file mode 100644 index 000000000..aa89d67d7 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/restconfdiscovery/TestRestconfDiscoveryNode.java @@ -0,0 +1,175 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.restconfdiscovery; + +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.media.sse.SseFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class TestRestconfDiscoveryNode { + + private static final URI CONTEXT = URI.create("http://localhost:8080/"); + + @Test + public void sendRequest() throws SvcLogicException, InterruptedException, IOException { + final ResourceConfig resourceConfig = new ResourceConfig( + SseServerMock.class, SseFeature.class); + HttpServer server = GrizzlyHttpServerFactory.createHttpServer(CONTEXT, + resourceConfig); + server.start(); + RestconfApiCallNode restconf = mock(RestconfApiCallNode.class); + doNothing().when(restconf) + .sendRequest(any(Map.class), any(SvcLogicContext.class)); + RestapiCallNode restApi = new RestapiCallNode(); + doReturn(restApi).when(restconf).getRestapiCallNode(); + + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("prop.encoding-json", "encoding-json"); + ctx.setAttribute("restapi-result.response-code", "200"); + ctx.setAttribute("restapi-result.ietf-subscribed-notifications" + + ":establish-subscription.output.identifier", + "89"); + + Map<String, String> p = new HashMap<>(); + p.put("sseConnectURL", "http://localhost:8080/events"); + p.put("subscriberId", "networkId"); + p.put("responsePrefix", "restapi-result"); + p.put("restapiUser", "access"); + p.put("restapiPassword", "abc@123"); + p.put("customHttpHeaders", "X-ACCESS-TOKEN=x-ik2ps4ikvzupbx0486ft" + + "1ebzs7rt85futh9ho6eofy3wjsap7wqktemlqm4bbsmnar3vrtbyrzuk" + + "bv5itd6m1cftldpjarnyle3sdcqq9hftc4lebz464b5ffxmlbvg9"); + p.put("restapiUrl", "https://localhost:8080/restconf/operations/" + + "ietf-subscribed-notifications:establish-subscription"); + p.put("module", "testmodule"); + p.put("rpc", "testrpc"); + p.put("version", "1.0"); + p.put("mode", "sync"); + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode(restconf); + rdn.establishSubscription(p, ctx); + Thread.sleep(1000); + rdn.deleteSubscription(p, ctx); + server.shutdown(); + } + + @Test(expected = SvcLogicException.class) + public void testSubGraphExecution() throws SvcLogicException{ + SvcLogicGraphInfo subDg = new SvcLogicGraphInfo(); + subDg.mode("sync"); + subDg.module("l3VpnService"); + subDg.rpc("createVpn"); + subDg.version("1.0"); + SvcLogicContext ctx = new SvcLogicContext(); + subDg.executeGraph(ctx); + } + + @Test(expected = SvcLogicException.class) + public void testEstablishSubscriptionWithoutSubscriberId() + throws SvcLogicException{ + SvcLogicContext ctx = new SvcLogicContext(); + Map<String, String> p = new HashMap<>(); + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( + new RestconfApiCallNode(new RestapiCallNode())); + rdn.establishSubscription(p, ctx); + } + + @Test + public void testResponseCode() { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("restapi-result.response-code", "200"); + ctx.setAttribute("response-code", "404"); + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( + new RestconfApiCallNode(new RestapiCallNode())); + assertThat(rdn.getResponseCode("restapi-result", ctx), + is("200")); + assertThat(rdn.getResponseCode(null, ctx), + is("404")); + } + + @Test + public void testOutputIdentifier() { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("restapi-result.ietf-subscribed-notifications:" + + "establish-subscription.output.identifier", + "89"); + ctx.setAttribute("ietf-subscribed-notifications:establish-subscripti" + + "on.output.identifier", "89"); + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( + new RestconfApiCallNode(new RestapiCallNode())); + assertThat(rdn.getOutputIdentifier("restapi-result", ctx), + is("89")); + } + + @Test + public void testGetTokenId() { + String customHttpHeaders = "X-ACCESS-TOKEN=x-ik2ps4ikvzupbx0486ft1ebzs7rt85" + + "futh9ho6eofy3wjsap7wqktemlqm4bbsmnar3vrtbyrzukbv5itd6m1cftldpjarny" + + "le3sdcqq9hftc4lebz464b5ffxmlbvg9"; + RestconfDiscoveryNode rdn = new RestconfDiscoveryNode( + new RestconfApiCallNode(new RestapiCallNode())); + + assertThat(rdn.getTokenId(customHttpHeaders), + is("x-ik2ps4ikvzupbx0486ft1ebzs7rt85futh9ho6eofy3wjsap7wqkt" + + "emlqm4bbsmnar3vrtbyrzukbv5itd6m1cftldpjarnyle3sdcqq9h" + + "ftc4lebz464b5ffxmlbvg9")); + } + + @Test + public void testSubscriptionInfo() throws SvcLogicException { + SubscriptionInfo info = new SubscriptionInfo(); + info.subscriberId("network-id"); + info.subscriptionId("8"); + info.filterUrl("/ietf-interfaces:interfaces"); + info.yangFilePath("/opt/yang"); + SvcLogicGraphInfo svcLogicGraphInfo = new SvcLogicGraphInfo(); + svcLogicGraphInfo.mode("sync"); + svcLogicGraphInfo.module("testModule"); + svcLogicGraphInfo.rpc("testRpc"); + svcLogicGraphInfo.version("1.0"); + info.callBackDG(svcLogicGraphInfo); + assertThat(info.subscriberId(), is("network-id")); + assertThat(info.subscriptionId(), is("8")); + assertThat(info.filterUrl(), is("/ietf-interfaces:interfaces")); + assertThat(info.yangFilePath(), is("/opt/yang")); + assertThat(info.callBackDG().module(), is("testModule")); + assertThat(info.callBackDG().mode(), is("sync")); + assertThat(info.callBackDG().rpc(), is("testRpc")); + assertThat(info.callBackDG().version(), is("1.0")); + } +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerTest.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerTest.java new file mode 100644 index 000000000..aa1e50d6a --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatSerializerTest.java @@ -0,0 +1,996 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.HttpResponse; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.GET; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PATCH; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.POST; +import static org.onap.ccsdk.sli.plugins.restapicall.HttpMethod.PUT; +import static org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiUtils.parseUrl; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.DECODE_ANYXML_RESPONSE; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.DECODE_FROM_JSON_RPC; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.DECODE_FROM_XML_RPC; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_ANYXML; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_ID_PUT; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_RPC; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_YANG; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_YANG_AUG_POST; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_JSON_YANG_PUT; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_ID_PUT; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_RPC; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_YANG; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_YANG_AUG_POST; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.ENCODE_TO_XML_YANG_PUT; + + +/** + * Unit test cases for data format serialization and restconf api call node. + */ +public class DataFormatSerializerTest { + + private Map<String, String> p; + + private RestconfApiCallNode restconf; + + private RestapiCallNode restApi; + + private DfCaptor dfCaptor; + + /** + * Sets up the pre-requisite for each test case. + * + * @throws SvcLogicException when test case fails + */ + @Before + public void setUp() throws SvcLogicException { + p = new HashMap<>(); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + restApi = new RestapiCallNode(); + restconf = mock(RestconfApiCallNode.class); + dfCaptor = new DfCaptor(); + createMethodMocks(); + } + + /** + * Creates method mocks using mockito for RestconfApiCallNode class. + * + * @throws SvcLogicException when test case fails + */ + private void createMethodMocks() throws SvcLogicException { + doReturn(restApi).when(restconf).getRestapiCallNode(); + doCallRealMethod().when(restconf).sendRequest( + any(Map.class), any(SvcLogicContext.class)); + doCallRealMethod().when(restconf).sendRequest( + any(Map.class), any(SvcLogicContext.class), any(Integer.class)); + doAnswer(dfCaptor).when(restconf).serializeRequest( + any(Map.class), any(YangParameters.class), any(String.class), + any(InstanceIdentifierContext.class)); + doAnswer(dfCaptor).when(restconf).updateReq( + any(String.class), any(YangParameters.class), + any(InstanceIdentifierContext.class)); + } + + /** + * Creates mock using mockito with input data for decoding. + * + * @param decodeData input data + * @throws SvcLogicException when test case fails + */ + private void createMockForDecode(String decodeData) + throws SvcLogicException { + doReturn(decodeData).when(restconf).getResponse( + any(SvcLogicContext.class), any(YangParameters.class), + any(String.class), any(HttpResponse.class)); + doCallRealMethod().when(restconf).serializeResponse( + any(YangParameters.class), any(String.class), any(String.class), + any(InstanceIdentifierContext.class)); + } + + /** + * Verifies encoding of parameters to JSON data format with identity-ref + * and inter-file linking. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonId() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + ctx.setAttribute(pre + "l", "abc"); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_ID)); + } + + /** + * Verifies encoding of parameters to JSON data format any xml in it. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeForAnyXml() throws SvcLogicException { + String pre = "execution-service_process."; + SvcLogicContext ctx = createAnyXmlAttList(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/api/v1/execution-service/process"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_ANYXML)); + } + + /** + * Verifies encoding of parameters to JSON data format with identity-ref + * and inter-file linking for put operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonIdWithPut() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + ctx.setAttribute(pre + "l", "abc"); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_ID_PUT)); + } + + /** + * Verifies encoding of parameters to JSON data format with identity-ref + * and inter-file linking for patch operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonIdWithPatch() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + ctx.setAttribute(pre + "l", "abc"); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "patch"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_ID_PUT)); + } + + /** + * Verifies encoding of parameters to XML data format with identity-ref + * and inter-file linking. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlId() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_ID)); + } + + /** + * Verifies encoding of parameters to XML data format with identity-ref + * and inter-file linking for put operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlIdWithPut() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_ID_PUT)); + } + + /** + * Verifies encoding of parameters to XML data format with identity-ref + * and inter-file linking for patch operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlIdWithPatch() throws SvcLogicException { + String pre = "identity-test_test."; + SvcLogicContext ctx = createAttList(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "patch"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_ID_PUT)); + } + + /** + * Verifies decoding of parameters from JSON data format with identity-ref + * and inter-file linking. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToJsonId() throws SvcLogicException { + createMockForDecode(ENCODE_TO_JSON_ID); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "identity-test_test."; + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + assertThat(ctx.getAttribute(pre + "l"), is("abc")); + verifyAttList(ctx, pre); + } + + /** + * Verifies decoding of parameters from XML data format with identity-ref + * and inter-file linking. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToXmlId() throws SvcLogicException { + createMockForDecode(ENCODE_TO_XML_ID); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "identity-test_test."; + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/identity-test:test"); + restconf.sendRequest(p, ctx); + verifyAttList(ctx, pre); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYang() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG)); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment for put operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYangWithPut() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_PUT)); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment for patch operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYangWithPatch() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "patch"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_PUT)); + } + + /** + * Verifies encoding of parameters to JSON data format with augment as + * root child. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonWithAugAsRootChild() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_AUG_POST)); + } + + /** + * Verifies decoding of parameters from JSON data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToJsonYang() throws SvcLogicException { + createMockForDecode(ENCODE_TO_JSON_YANG); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "test-yang_cont1.cont2."; + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1"); + restconf.sendRequest(p, ctx); + verifyAttListYang(ctx, pre); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYang() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG)); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment for put operation-type + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYangWithPut() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_PUT)); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment for patch operation-type + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYangWithPatch() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_PUT)); + } + + /** + * Verifies encoding of parameters to XML data format with augment as + * root child. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlWithAugAsRootChild() throws SvcLogicException { + String pre = "test-yang_cont1.cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1/cont2/cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_AUG_POST)); + } + + /** + * Verifies decoding of parameters from XML data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToXmlYang() throws SvcLogicException { + createMockForDecode(ENCODE_TO_XML_YANG); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "test-yang_cont1.cont2."; + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:cont1"); + restconf.sendRequest(p, ctx); + verifyAttListYang(ctx, pre); + } + + /** + * Verifies encoding of and decoding from, JSON respectively for data + * format with containers, grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void codecToJsonRpc() throws SvcLogicException { + createMockForDecode(DECODE_FROM_JSON_RPC); + String inPre = "test-yang_create-sfc.input."; + String outPre = "test-yang_create-sfc.output."; + SvcLogicContext ctx = createAttListRpc(inPre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:create-sfc"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_RPC)); + verifyAttListRpc(ctx, outPre); + } + + /** + * Verifies encoding of and decoding from, JSON for ANYXML. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void codecForNormalAnyXml() throws SvcLogicException { + createMockForDecode(DECODE_ANYXML_RESPONSE); + String inPre = "execution-service_process."; + SvcLogicContext ctx = createAnyXmlAttList(inPre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("responsePrefix", "pp"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/api/v1/execution-service/process"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_ANYXML)); + verifyOutputOfAnyXml(ctx); + } + + /** + * Verifies encoding of and decoding from, XML respectively for data + * format with containers, grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void codecToXmlRpc() throws SvcLogicException { + createMockForDecode(DECODE_FROM_XML_RPC); + String inPre = "test-yang_create-sfc.input."; + String outPre = "test-yang_create-sfc.output."; + SvcLogicContext ctx = createAttListRpc(inPre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test-yang:create-sfc"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_RPC)); + verifyAttListRpc(ctx, outPre); + } + + /** + * Verifies URL parser returning path with only schema information for all + * kind of URL. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void validateUrlParser() throws SvcLogicException { + String actVal = "identity-test:test"; + String putId = "/for-put"; + String url1 = "http://echo.getpostman.com/restconf/operations/" + + actVal; + String url2 = "http://echo.getpostman.com/restconf/data/" + actVal; + String url3 = "https://echo.getpostman.com/restconf/operations/" + + actVal; + String url4 = "https://echo.getpostman.com/restconf/data/" + actVal + + putId; + String url5 = "http://localhost:8282/restconf/operations/" + actVal; + String url6 = "https://localhost:8282/restconf/operations/" + actVal; + String url7 = "http://localhost:8282/restconf/data/" + actVal + + putId; + String url8 = "https://localhost:8282/restconf/data/" + actVal; + String url9 = "http://182.2.61.24:2250/restconf/data/" + actVal; + String url10 = "https://182.2.61.24:2250/restconf/operations/" + actVal; + String url11 = "https://182.2.61.24:2250/api/v1/execution-service" + + "/process"; + String url12 = "https://182.2.61.24:2250/api/v1/execution-service" + + "/process/payload"; + String url13 = "https://182.2.61.24:2250/api/v1/execution-service" + + "/process/payload/"; + String val1 = parseUrl(url1, POST); + String val2 = parseUrl(url2, GET); + String val3 = parseUrl(url3, PATCH); + String val4 = parseUrl(url4, PUT); + String val5 = parseUrl(url5, GET); + String val6 = parseUrl(url6, POST); + String val7 = parseUrl(url7, PUT); + String val8 = parseUrl(url8, POST); + String val9 = parseUrl(url9, GET); + String val10 = parseUrl(url10, POST); + String val11 = parseUrl(url11, POST); + String val12 = parseUrl(url12, POST); + String val13 = parseUrl(url13, POST); + assertThat(val1, is(actVal)); + assertThat(val2, is(actVal)); + assertThat(val3, is(actVal)); + assertThat(val4, is(actVal + putId)); + assertThat(val5, is(actVal)); + assertThat(val6, is(actVal)); + assertThat(val7, is(actVal + putId)); + assertThat(val8, is(actVal)); + assertThat(val9, is(actVal)); + assertThat(val10, is(actVal)); + assertThat(val11, is("execution-service:process")); + assertThat(val12, is("execution-service:process/payload")); + assertThat(val13, is("execution-service:process/payload/")); + } + + /** + * Creates attribute list for encoding JSON or XML with ANYXML YANG + * file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAnyXmlAttList(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + String pre1 = pre + "commonHeader."; + String pre2 = pre + "actionIdentifiers."; + ctx.setAttribute(pre + "isNonAppend", "true"); + ctx.setAttribute(pre1 + "originatorId", "SDNC_DG"); + ctx.setAttribute(pre1 + "requestId", "123456-1000"); + ctx.setAttribute(pre1 + "subRequestId", "sub-123456-1000"); + ctx.setAttribute(pre2 + "blueprintName", + "baseconfiguration"); + ctx.setAttribute(pre2 + "blueprintVersion", "1.0.0"); + ctx.setAttribute(pre2 + "actionName", "assign-activate"); + ctx.setAttribute(pre2 + "mode", "sync"); + ctx.setAttribute(pre + "payload." + + "template-prefix", "vDNS-test"); + ctx.setAttribute(pre + "payload.resource-assignment-request" + + ".resource-assignment-properties", + "{\n" + + " \"service-instance-id\": " + + "\"1234\",\n" + + " \"vnf-id\": \"3526\",\n" + + " \"customer-name\": \"htipl\",\n" + + " \"subscriber-name\": \"huawei\"\n" + + " }"); + return ctx; + } + + /** + * Creates attribute list for encoding JSON or XML with identity-ref YANG + * file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAttList(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + String pre1 = pre + "con1.interfaces."; + ctx.setAttribute(pre + "con1.interface", "identity-types:physical"); + ctx.setAttribute(pre1 + "int-list[0].iden", "optical"); + ctx.setAttribute(pre1 + "int-list[0].available.ll[0]", "Giga"); + ctx.setAttribute(pre1 + "int-list[0].available.ll[1]", + "identity-types:Loopback"); + ctx.setAttribute(pre1 + "int-list[0].available.ll[2]", + "identity-types-second:Ethernet"); + ctx.setAttribute(pre1 + "int-list[0].available.leaf1", "58"); + ctx.setAttribute(pre1 + "int-list[0].available.leaf2", + "identity-types-second:iden2"); + + ctx.setAttribute(pre1 + "int-list[1].iden", "214748364"); + ctx.setAttribute(pre1 + "int-list[1].available.ll[0]", "Giga"); + ctx.setAttribute(pre1 + "int-list[1].available.ll[1]", + "identity-types:Loopback"); + ctx.setAttribute(pre1 + "int-list[1].available.ll[2]", + "identity-types-second:Ethernet"); + ctx.setAttribute(pre1 + "int-list[1].available.leaf1", + "8888"); + ctx.setAttribute(pre1 + "int-list[1].available.leaf2", + "identity-types-second:iden2"); + return ctx; + } + + /** + * Creates attribute list for encoding JSON or XML with container, + * grouping and augmented YANG file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAttListYang(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute(pre + "cont3.leaf10", "abc"); + ctx.setAttribute(pre + "list1[0].leaf1", "true"); + ctx.setAttribute(pre + "list1[0].leaf2", "abc"); + ctx.setAttribute(pre + "list1[0].leaf3", "abc"); + ctx.setAttribute(pre + "list1[0].ll1[0]", "abc"); + ctx.setAttribute(pre + "list1[0].ll1[1]", "abc"); + ctx.setAttribute(pre + "list1[0].ll2[0]", "abc"); + ctx.setAttribute(pre + "list1[0].ll2[1]", "abc"); + ctx.setAttribute(pre + "list1[0].cont4.leaf11", "abc"); + ctx.setAttribute(pre + "list1[0].list4[0].leaf8", "abc"); + ctx.setAttribute(pre + "list1[0].list4[1].leaf8", "abc"); + ctx.setAttribute(pre + "list1[0].list5[0].leaf9", "abc"); + ctx.setAttribute(pre + "list1[0].list5[1].leaf9", "abc"); + ctx.setAttribute(pre + "list1[1].leaf1", "true"); + ctx.setAttribute(pre + "list1[1].leaf2", "abc"); + ctx.setAttribute(pre + "list1[1].leaf3", "abc"); + ctx.setAttribute(pre + "list1[1].ll1[0]", "abc"); + ctx.setAttribute(pre + "list1[1].ll1[1]", "abc"); + ctx.setAttribute(pre + "list1[1].ll2[0]", "abc"); + ctx.setAttribute(pre + "list1[1].ll2[1]", "abc"); + ctx.setAttribute(pre + "list1[1].cont4.leaf11", "abc"); + ctx.setAttribute(pre + "list1[1].list4[0].leaf8", "abc"); + ctx.setAttribute(pre + "list1[1].list4[1].leaf8", "abc"); + ctx.setAttribute(pre + "list1[1].list5[0].leaf9", "abc"); + ctx.setAttribute(pre + "list1[1].list5[1].leaf9", "abc"); + ctx.setAttribute(pre + "list2[0].leaf4", "abc"); + ctx.setAttribute(pre + "list2[1].leaf4", "abc"); + ctx.setAttribute(pre + "leaf5", "abc"); + ctx.setAttribute(pre + "leaf6", "abc"); + ctx.setAttribute(pre + "ll3[0]", "abc"); + ctx.setAttribute(pre + "ll3[1]", "abc"); + ctx.setAttribute(pre + "ll4[0]", "abc"); + ctx.setAttribute(pre + "ll4[1]", "abc"); + ctx.setAttribute(pre + "cont4.leaf10", "abc"); + ctx.setAttribute(pre + "list6[0].leaf11", "abc"); + ctx.setAttribute(pre + "list6[1].leaf11", "abc"); + ctx.setAttribute(pre + "leaf12", "abc"); + ctx.setAttribute(pre + "ll5[0]", "abc"); + ctx.setAttribute(pre + "ll5[1]", "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont5.leaf13", "true"); + ctx.setAttribute(pre + "cont4.test-augment_list7[0].leaf14", "test"); + ctx.setAttribute(pre + "cont4.test-augment_list7[1].leaf14", "create"); + ctx.setAttribute(pre + "cont4.test-augment_leaf15", "abc"); + ctx.setAttribute(pre + "cont4.test-augment_ll6[0]", "unbounded"); + ctx.setAttribute(pre + "cont4.test-augment_ll6[1]", "8"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.cont12.leaf26", + "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.list9[0].leaf27", + "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.list9[1].leaf27", + "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.leaf28", "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.ll9[0]", "abc"); + ctx.setAttribute(pre + "cont4.test-augment_cont13.ll9[1]", "abc"); + return ctx; + } + + /** + * Creates attribute list for encoding JSON or XML with RPC YANG file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAttListRpc(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute(pre + "cont14.leaf28", "abc"); + ctx.setAttribute(pre + "list10[0].leaf29", "abc"); + ctx.setAttribute(pre + "list10[1].leaf29", "abc"); + ctx.setAttribute(pre + "leaf30", "abc"); + ctx.setAttribute(pre + "ll10[0]", "abc"); + ctx.setAttribute(pre + "ll10[1]", "abc"); + ctx.setAttribute(pre + "cont15.leaf31", "abc"); + ctx.setAttribute(pre + "cont13.list9[0].leaf27", "abc"); + ctx.setAttribute(pre + "cont13.list9[1].leaf27", "abc"); + ctx.setAttribute(pre + "cont13.leaf28", "abc"); + ctx.setAttribute(pre + "cont13.ll9[0]", "abc"); + ctx.setAttribute(pre + "cont13.ll9[1]", "abc"); + return ctx; + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * identity-ref YANG file. + * + * @param ctx service logic context + * @param pre prefix + */ + private void verifyAttList(SvcLogicContext ctx, String pre) { + String pre1 = pre + "con1.interfaces."; + assertThat(ctx.getAttribute(pre + "con1.interface"), is( + "identity-types:physical")); + assertThat(ctx.getAttribute(pre + "con1.interface"), is( + "identity-types:physical")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].iden"), is("optical")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].available.ll[0]"), is( + "Giga")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].available.ll[1]"), is( + "identity-types:Loopback")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].available.ll[2]"), is( + "identity-types-second:Ethernet")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].available.leaf1"), is( + "58")); + assertThat(ctx.getAttribute(pre1 + "int-list[0].available.leaf2"), is( + "identity-types-second:iden2")); + + assertThat(ctx.getAttribute(pre1 + "int-list[1].iden"), is( + "214748364")); + assertThat(ctx.getAttribute(pre1 + "int-list[1].available.ll[0]"), is( + "Giga")); + assertThat(ctx.getAttribute(pre1 + "int-list[1].available.ll[1]"), is( + "identity-types:Loopback")); + assertThat(ctx.getAttribute(pre1 + "int-list[1].available.ll[2]"), is( + "identity-types-second:Ethernet")); + assertThat(ctx.getAttribute(pre1 + "int-list[1].available.leaf1"), is( + "8888")); + assertThat(ctx.getAttribute(pre1 + "int-list[1].available.leaf2"), is( + "identity-types-second:iden2")); + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * container, grouping and augmented file. + * + * @param ctx service logic context + * @param pre prefix + */ + private void verifyAttListYang(SvcLogicContext ctx, String pre) { + assertThat(ctx.getAttribute(pre + "cont3.leaf10"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].leaf1"), is("true")); + assertThat(ctx.getAttribute(pre + "list1[0].leaf2"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].leaf3"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].ll1[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].ll1[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].ll2[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].ll2[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].cont4.leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].list4[0].leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].list4[1].leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].list5[0].leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[0].list5[1].leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].leaf1"), is("true")); + assertThat(ctx.getAttribute(pre + "list1[1].leaf2"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].leaf3"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].ll1[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].ll1[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].ll2[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].ll2[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].cont4.leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].list4[0].leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].list4[1].leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].list5[0].leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list1[1].list5[1].leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "list2[0].leaf4"), is("abc")); + assertThat(ctx.getAttribute(pre + "list2[1].leaf4"), is("abc")); + assertThat(ctx.getAttribute(pre + "leaf5"), is("abc")); + assertThat(ctx.getAttribute(pre + "leaf6"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll3[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll3[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll4[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll4[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.leaf10"), is( "abc")); + assertThat(ctx.getAttribute(pre + "list6[0].leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "list6[1].leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "leaf12"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll5[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll5[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont5.leaf13"), + is("true")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_list7[0].leaf14"), + is("test")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_list7[1].leaf14"), + is("create")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_leaf15"), + is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_ll6[0]"), + is("unbounded")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_ll6[1]"), + is("8")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13" + + ".cont12.leaf26"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13.list9[0]" + + ".leaf27"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13.list9[1]" + + ".leaf27"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13.leaf28"), + is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13.ll9[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "cont4.test-augment_cont13.ll9[1]"), + is("abc")); + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * RPC YANG file. + * + * @param ctx service logic context + * @param pre prefix + */ + private void verifyAttListRpc(SvcLogicContext ctx, String pre) { + assertThat(ctx.getAttribute(pre + "cont16.leaf32"), is("abc")); + assertThat(ctx.getAttribute(pre + "list11[0].leaf33"), is("abc")); + assertThat(ctx.getAttribute(pre + "list11[1].leaf33"), is("abc")); + assertThat(ctx.getAttribute(pre + "leaf34"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll11[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "ll11[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont17.leaf35"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.cont12.leaf26"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.list9[0].leaf27"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.list9[1].leaf27"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.ll9[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.ll9[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "cont13.leaf28"), is("abc")); + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * ANYXML YANG file. + * + * @param ctx service logic context + */ + private void verifyOutputOfAnyXml(SvcLogicContext ctx) { + System.out.println(ctx.getAttribute("pp.status.eventType")); + assertThat(ctx.getAttribute("pp.status.eventType"), is( + "EVENT_COMPONENT_EXECUTED")); + assertThat(ctx.getAttribute("pp.actionIdentifiers.blueprintName"), + is("golden")); + assertThat(ctx.getAttribute("pp.actionIdentifiers.mode"), + is("sync")); + assertThat(ctx.getAttribute("pp.stepData.name"), + is("resource-assignment")); + assertThat(ctx.getAttribute("pp.status.message"), + is("success")); + assertThat(ctx.getAttribute("pp.commonHeader.originatorId"), + is("System")); + assertThat(ctx.getAttribute("pp.status.code"), + is("200")); + assertThat(ctx.getAttribute("pp.commonHeader.requestId"), + is("1234")); + assertThat(ctx.getAttribute("pp.commonHeader.subRequestId"), + is("1234-12234")); + assertThat(ctx.getAttribute("pp.commonHeader.timestamp"), + is("2019-05-18T23:42:41.658Z")); + assertThat(ctx.getAttribute("pp.status.timestamp"), + is("2019-05-18T23:42:41.950Z")); + assertThat(ctx.getAttribute("pp.actionIdentifiers.blueprintV" + + "ersion"), is("1.0.0")); + assertThat(ctx.getAttribute("pp.actionIdentifiers.actionName"), + is("resource-assignment")); + assertThat(ctx.getAttribute("pp.payload.resource-assignment-resp" + + "onse.meshed-template.vf-module-1"), + is("<interface>\n <description>This i" + + "s the Virtual Firewall entity</description>\n" + + " <vfw>10.0.101.20/24</vfw>\n" + + "</interface>")); + assertThat(ctx.getAttribute("pp.actionIdentifiers.actionName"), + is("resource-assignment")); + } + + + /** + * Captures the data format messages by mocking it, which can be used in + * testing the value. + * + * @param <String> capturing data format + */ + public class DfCaptor<String> implements Answer { + + private String result; + + /** + * Returns the captured data format message. + * + * @return data format message. + */ + public String getResult() { + return result; + } + + @Override + public String answer(InvocationOnMock invocationOnMock) + throws Throwable { + result = (String) invocationOnMock.callRealMethod(); + return result; + } + } + +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatUtilsTest.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatUtilsTest.java new file mode 100644 index 000000000..c1bb71985 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/DataFormatUtilsTest.java @@ -0,0 +1,631 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +/** + * Unit test case utilities for data format serializer and restconf api call + * node. + */ +public final class DataFormatUtilsTest { + + static final String ENCODE_TO_JSON_ID_COMMON = "\n \"interfaces\"" + + ": " + + "{\n" + + " \"int-list\": [\n" + + " {\n" + + " \"iden\": \"optical\",\n" + + " \"available\": {\n" + + " \"ll\": [\n" + + " \"Giga\",\n" + + " \"identity-types:Loopback\",\n" + + " \"identity-types-second:Ethernet" + + "\"\n" + + " ],\n" + + " \"leaf1\": \"58\",\n" + + " \"leaf2\": \"identity-types-second:iden" + + "2\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"iden\": \"214748364\",\n" + + " \"available\": {\n" + + " \"ll\": [\n" + + " \"Giga\",\n" + + " \"identity-types:Loopback\",\n" + + " \"identity-types-second:Ethernet" + + "\"\n" + + " ],\n" + + " \"leaf1\": \"8888\",\n" + + " \"leaf2\": \"identity-types-second:ide" + + "n2\"\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"interface\": \"identity-types:physical\"\n" + + " }"; + + static final String ENCODE_TO_JSON_ID = "{\n" + + " \"identity-test:con1\": {" + ENCODE_TO_JSON_ID_COMMON + + ",\n" + + " \"identity-test:l\": \"abc\"\n" + + "}"; + + static final String ENCODE_TO_ANYXML = "{\n" + + " \"actionIdentifiers\": {\n" + + " \"mode\": \"sync\",\n" + + " \"blueprintName\": \"baseconfiguration\",\n" + + " \"blueprintVersion\": \"1.0.0\",\n" + + " \"actionName\": \"assign-activate\"\n" + + " },\n" + + " \"payload\": {\n" + + " \"template-prefix\": \"vDNS-test\",\n" + + " \"resource-assignment-request\": {\n" + + " \"resource-assignment-properties\": {\n" + + " \"service-instance-id\": \"1234\",\n" + + " \"vnf-id\": \"3526\",\n" + + " \"customer-name\": \"htipl\",\n" + + " \"subscriber-name\": \"huawei\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"commonHeader\": {\n" + + " \"subRequestId\": \"sub-123456-1000\",\n" + + " \"requestId\": \"123456-1000\",\n" + + " \"originatorId\": \"SDNC_DG\"\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_JSON_ID_PUT = "{\n" + + " \"identity-test:test\": {\n" + + " \"con1\": {" + addSpace(ENCODE_TO_JSON_ID_COMMON, 4) + + ",\n" + + " \"l\": \"abc\"\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_XML_ID_COMMON = "\n <interfaces>\n" + + " <int-list>\n" + + " <iden>optical</iden>\n" + + " <available>\n" + + " <ll>Giga</ll>\n" + + " <ll xmlns:yangid=\"identity:list:ns:test:json:se" + + "r\">yangid:Loopback</ll>\n" + + " <ll xmlns:yangid=\"identity:list:second:ns:test" + + ":json:ser\">yangid:Ethernet</ll>\n" + + " <leaf1>58</leaf1>\n" + + " <leaf2 xmlns:yangid=\"identity:list:second:ns:t" + + "est:json:ser\">yangid:iden2</leaf2>\n" + + " </available>\n" + + " </int-list>\n" + + " <int-list>\n" + + " <iden>214748364</iden>\n" + + " <available>\n" + + " <ll>Giga</ll>\n" + + " <ll xmlns:yangid=\"identity:list:ns:test:json:s" + + "er\">yangid:Loopback</ll>\n" + + " <ll xmlns:yangid=\"identity:list:second:ns:test" + + ":json:ser\">yangid:Ethernet</ll>\n" + + " <leaf1>8888</leaf1>\n" + + " <leaf2 xmlns:yangid=\"identity:list:second:ns:t" + + "est:json:ser\">yangid:iden2</leaf2>\n" + + " </available>\n" + + " </int-list>\n" + + " </interfaces>\n" + + " <interface xmlns:yangid=\"identity:list:ns:test:json:ser\">" + + "yangid:physical</interface>"; + + static final String ENCODE_TO_XML_ID = "<?xml version=\"1.0\" encoding=" + + "\"UTF-8\" standalone=\"no\"?>\n" + + "<con1 xmlns=\"identity:ns:test:json:ser\">" + + ENCODE_TO_XML_ID_COMMON + "\n</con1>\n"; + + static final String ENCODE_TO_XML_ID_PUT = "<?xml version=\"1.0\" enco" + + "ding=\"UTF-8\" standalone=\"no\"?>\n" + + "<test xmlns=\"identity:ns:test:json:ser\">\n" + + " <con1>" + addSpace(ENCODE_TO_XML_ID_COMMON, 4) + + "\n </con1>\n" + + "</test>\n"; + + static final String ENCODE_TO_JSON_YANG_COMMON = "\n " + + "\"test-augment:ll6\": [\n" + + " \"unbounded\",\n" + + " \"8\"\n" + + " ],\n" + + " \"test-augment:cont13\": {\n" + + " \"ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"list9\": [\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"leaf28\": \"abc\",\n" + + " \"cont12\": {\n" + + " \"leaf26\": \"abc\"\n" + + " }\n" + + " },\n" + + " \"test-augment:list7\": [\n" + + " {\n" + + " \"leaf14\": \"test\"\n" + + " },\n" + + " {\n" + + " \"leaf14\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"test-augment:leaf15\": \"abc\",\n" + + " \"test-augment:cont5\": {\n" + + " \"leaf13\": \"true\"\n" + + " }"; + + static final String ENCODE_TO_JSON_YANG_AUG_POST = "{\n" + + " \"test-yang:leaf10\": \"abc\"," + + ENCODE_TO_JSON_YANG_COMMON + "\n}"; + + static final String ENCODE_TO_JSON_YANG = "{\n" + + " \"test-yang:cont2\": {\n" + + " \"list1\": [\n" + + " {\n" + + " \"ll1\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"leaf1\": \"true\",\n" + + " \"ll2\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"list5\": [\n" + + " {\n" + + " \"leaf9\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf9\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"leaf3\": \"abc\",\n" + + " \"leaf2\": \"abc\",\n" + + " \"list4\": [\n" + + " {\n" + + " \"leaf8\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf8\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"cont4\": {\n" + + " \"leaf11\": \"abc\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"ll1\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"leaf1\": \"true\",\n" + + " \"ll2\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"leaf3\": \"abc\",\n" + + " \"list5\": [\n" + + " {\n" + + " \"leaf9\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf9\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"list4\": [\n" + + " {\n" + + " \"leaf8\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf8\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"leaf2\": \"abc\",\n" + + " \"cont4\": {\n" + + " \"leaf11\": \"abc\"\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"ll3\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"ll5\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"cont4\": {\n" + + " \"leaf10\": \"abc\"," + + addSpace(ENCODE_TO_JSON_YANG_COMMON, 8) + "\n" + + " },\n" + + " \"ll4\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"cont3\": {\n" + + " \"leaf10\": \"abc\"\n" + + " },\n" + + " \"leaf5\": \"abc\",\n" + + " \"list2\": [\n" + + " {\n" + + " \"leaf4\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf4\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"leaf12\": \"abc\",\n" + + " \"leaf6\": \"abc\",\n" + + " \"list6\": [\n" + + " {\n" + + " \"leaf11\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf11\": \"abc\"\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_JSON_YANG_PUT = "{\n" + + " \"test-yang:cont4\": {" + addSpace( + ENCODE_TO_JSON_YANG_COMMON, 4) + ",\n" + + " \"leaf10\": \"abc\"\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_XML_YANG_COMMON = "\n" + + "<ll6 xmlns=\"urn:opendaylight:params:xml:ns:yang:aug" + + "ment\">unbounded</ll6>\n" + + "<ll6 xmlns=\"urn:opendaylight:params:xml:ns:yang:aug" + + "ment\">8</ll6>\n" + + "<cont13 xmlns=\"urn:opendaylight:params:xml:ns:yang:" + + "augment\">\n" + + " <ll9>abc</ll9>\n" + + " <ll9>abc</ll9>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <leaf28>abc</leaf28>\n" + + " <cont12>\n" + + " <leaf26>abc</leaf26>\n" + + " </cont12>\n" + + "</cont13>\n" + + "<list7 xmlns=\"urn:opendaylight:params:xml:ns:yang:a" + + "ugment\">\n" + + " <leaf14>test</leaf14>\n" + + "</list7>\n" + + "<list7 xmlns=\"urn:opendaylight:params:xml:ns:yang:a" + + "ugment\">\n" + + " <leaf14>create</leaf14>\n" + + "</list7>\n" + + "<leaf15 xmlns=\"urn:opendaylight:params:xml:ns:yang:" + + "augment\">abc</leaf15>\n" + + "<cont5 xmlns=\"urn:opendaylight:params:xml:ns:yang:a" + + "ugment\">\n" + + " <leaf13>true</leaf13>\n" + + "</cont5>"; + + static final String ENCODE_TO_XML_YANG_AUG_POST = "<?xml version=\"1.0\"" + + " encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<leaf10 xmlns=\"urn:opendaylight:params:xml:ns:yang:test\">abc" + + "</leaf10>" + + ENCODE_TO_XML_YANG_COMMON + "\n"; + + static final String ENCODE_TO_XML_YANG_PUT = "<?xml version=\"1.0\" enc" + + "oding=\"UTF-8\" standalone=\"no\"?>\n" + + "<cont4 xmlns=\"urn:opendaylight:params:xml:ns:yang:test\">\n" + + " <leaf10>abc</leaf10>" + + addSpace(ENCODE_TO_XML_YANG_COMMON, 4) + "\n</cont4>\n"; + + static final String ENCODE_TO_XML_YANG = "<?xml version=\"1.0\" encoding" + + "=\"UTF-8\" standalone=\"no\"?>\n" + + "<cont2 xmlns=\"urn:opendaylight:params:xml:ns:yang:test\">\n" + + " <list1>\n" + + " <ll1>abc</ll1>\n" + + " <ll1>abc</ll1>\n" + + " <leaf1>true</leaf1>\n" + + " <ll2>abc</ll2>\n" + + " <ll2>abc</ll2>\n" + + " <list5>\n" + + " <leaf9>abc</leaf9>\n" + + " </list5>\n" + + " <list5>\n" + + " <leaf9>abc</leaf9>\n" + + " </list5>\n" + + " <leaf3>abc</leaf3>\n" + + " <leaf2>abc</leaf2>\n" + + " <list4>\n" + + " <leaf8>abc</leaf8>\n" + + " </list4>\n" + + " <list4>\n" + + " <leaf8>abc</leaf8>\n" + + " </list4>\n" + + " <cont4>\n" + + " <leaf11>abc</leaf11>\n" + + " </cont4>\n" + + " </list1>\n" + + " <list1>\n" + + " <ll1>abc</ll1>\n" + + " <ll1>abc</ll1>\n" + + " <leaf1>true</leaf1>\n" + + " <ll2>abc</ll2>\n" + + " <ll2>abc</ll2>\n" + + " <leaf3>abc</leaf3>\n" + + " <list5>\n" + + " <leaf9>abc</leaf9>\n" + + " </list5>\n" + + " <list5>\n" + + " <leaf9>abc</leaf9>\n" + + " </list5>\n" + + " <list4>\n" + + " <leaf8>abc</leaf8>\n" + + " </list4>\n" + + " <list4>\n" + + " <leaf8>abc</leaf8>\n" + + " </list4>\n" + + " <leaf2>abc</leaf2>\n" + + " <cont4>\n" + + " <leaf11>abc</leaf11>\n" + + " </cont4>\n" + + " </list1>\n" + + " <ll3>abc</ll3>\n" + + " <ll3>abc</ll3>\n" + + " <ll5>abc</ll5>\n" + + " <ll5>abc</ll5>\n" + + " <cont4>\n" + + " <leaf10>abc</leaf10>"+ + addSpace(ENCODE_TO_XML_YANG_COMMON, 8) + "\n" + + " </cont4>\n" + + " <ll4>abc</ll4>\n" + + " <ll4>abc</ll4>\n" + + " <cont3>\n" + + " <leaf10>abc</leaf10>\n" + + " </cont3>\n" + + " <leaf5>abc</leaf5>\n" + + " <list2>\n" + + " <leaf4>abc</leaf4>\n" + + " </list2>\n" + + " <list2>\n" + + " <leaf4>abc</leaf4>\n" + + " </list2>\n" + + " <leaf12>abc</leaf12>\n" + + " <leaf6>abc</leaf6>\n" + + " <list6>\n" + + " <leaf11>abc</leaf11>\n" + + " </list6>\n" + + " <list6>\n" + + " <leaf11>abc</leaf11>\n" + + " </list6>\n" + + "</cont2>\n"; + + static final String ENCODE_TO_JSON_RPC = "{\n" + + " \"test-yang:input\": {\n" + + " \"leaf30\": \"abc\",\n" + + " \"list10\": [\n" + + " {\n" + + " \"leaf29\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf29\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"cont15\": {\n" + + " \"leaf31\": \"abc\"\n" + + " },\n" + + " \"cont14\": {\n" + + " \"leaf28\": \"abc\"\n" + + " },\n" + + " \"cont13\": {\n" + + " \"list9\": [\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"leaf28\": \"abc\"\n" + + " },\n" + + " \"ll10\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ]\n" + + " }\n" + + "}"; + + static final String DECODE_FROM_JSON_RPC = "{\n" + + " \"test-yang:output\": {\n" + + " \"cont16\": {\n" + + " \"leaf32\": \"abc\"\n" + + " },\n" + + " \"list11\": [\n" + + " {\n" + + " \"leaf33\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf33\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"leaf34\": \"abc\",\n" + + " \"ll11\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"cont17\": {\n" + + " \"leaf35\": \"abc\"\n" + + " },\n" + + " \"cont13\": {\n" + + " \"cont12\": {\n" + + " \"leaf26\": \"abc\"\n" + + " },\n" + + " \"list9\": [\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"leaf27\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"leaf28\": \"abc\"\n" + + " }\n" + + " }\n" + + "}"; + + static final String DECODE_ANYXML_RESPONSE = "{\n" + + " \"commonHeader\": {\n" + + " \"timestamp\": \"2019-05-18T23:42:41.658Z\",\n" + + " \"originatorId\": \"System\",\n" + + " \"requestId\": \"1234\",\n" + + " \"subRequestId\": \"1234-12234\",\n" + + " \"flags\": null\n" + + " },\n" + + " \"actionIdentifiers\": {\n" + + " \"blueprintName\": \"golden\",\n" + + " \"blueprintVersion\": \"1.0.0\",\n" + + " \"actionName\": \"resource-assignment\",\n" + + " \"mode\": \"sync\"\n" + + " },\n" + + " \"status\": {\n" + + " \"code\": 200,\n" + + " \"eventType\": \"EVENT_COMPONENT_EXECUTED\",\n" + + " \"timestamp\": \"2019-05-18T23:42:41.950Z\",\n" + + " \"errorMessage\": null,\n" + + " \"message\": \"success\"\n" + + " },\n" + + " \"payload\": {\n" + + " \"resource-assignment-response\": {\n" + + " \"meshed-template\": {\n" + + " \"vf-module-1\": \"<interface>\\n " + + " <description>This is the Virtual Firewall entity</" + + "description>\\n <vfw>10.0.101.20/24</vfw>\\n</interface>\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"stepData\": {\n" + + " \"name\": \"resource-assignment\",\n" + + " \"properties\": {\n" + + " \"resource-assignment-params\": null,\n" + + " \"status\": null\n" + + " }\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_XML_RPC = "<?xml version=\"1.0\" encoding" + + "=\"UTF-8\" standalone=\"no\"?>\n" + + "<input xmlns=\"urn:opendaylight:params:xml:ns:yang:test\">\n" + + " <leaf30>abc</leaf30>\n" + + " <list10>\n" + + " <leaf29>abc</leaf29>\n" + + " </list10>\n" + + " <list10>\n" + + " <leaf29>abc</leaf29>\n" + + " </list10>\n" + + " <cont15>\n" + + " <leaf31>abc</leaf31>\n" + + " </cont15>\n" + + " <cont14>\n" + + " <leaf28>abc</leaf28>\n" + + " </cont14>\n" + + " <cont13>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <ll9>abc</ll9>\n" + + " <ll9>abc</ll9>\n" + + " <leaf28>abc</leaf28>\n" + + " </cont13>\n" + + " <ll10>abc</ll10>\n" + + " <ll10>abc</ll10>\n" + + "</input>\n"; + + static final String DECODE_FROM_XML_RPC = "<?xml version=\"1.0\" encodi" + + "ng=\"UTF-8\" standalone=\"no\"?>\n" + + "<output xmlns=\"urn:opendaylight:params:xml:ns:yang:test\">\n" + + " <cont16>\n" + + " <leaf32>abc</leaf32>\n" + + " </cont16>\n" + + " <list11>\n" + + " <leaf33>abc</leaf33>\n" + + " </list11>\n" + + " <list11>\n" + + " <leaf33>abc</leaf33>\n" + + " </list11>\n" + + " <leaf34>abc</leaf34>\n" + + " <ll11>abc</ll11>\n" + + " <ll11>abc</ll11>\n" + + " <cont17>\n" + + " <leaf35>abc</leaf35>\n" + + " </cont17>\n" + + " <cont13>\n" + + " <cont12>\n" + + " <leaf26>abc</leaf26>\n" + + " </cont12>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <list9>\n" + + " <leaf27>abc</leaf27>\n" + + " </list9>\n" + + " <ll9>abc</ll9>\n" + + " <ll9>abc</ll9>\n" + + " <leaf28>abc</leaf28>\n" + + " </cont13>\n" + + "</output>"; + + /** + * Adds the specified number of space required for a req in each line. + * + * @param req request message + * @param i number of space + * @return space appended string + */ + public static String addSpace(String req, int i) { + StringBuilder space = new StringBuilder(); + for (int sp = 0; sp < i; sp++) { + space = space.append(" "); + } + return req.replaceAll("\n", "\n" + space.toString()); + } +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationTest.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationTest.java new file mode 100644 index 000000000..1109d426c --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationTest.java @@ -0,0 +1,762 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.restapicall.HttpResponse; +import org.onap.ccsdk.sli.plugins.restapicall.RestapiCallNode; +import org.onap.ccsdk.sli.plugins.restconfapicall.RestconfApiCallNode; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.DECODE_FROM_JSON_RPC_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.DECODE_FROM_XML_RPC_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_JSON_RPC_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_JSON_WITH_AUG_PATH; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_JSON_YANG_AUG_POST_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_JSON_YANG_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_JSON_YANG_PUT_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_XML_RPC_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_XML_YANG_AUG_POST_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_XML_YANG_ID; +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.IdentifierValidationUtilsTest.ENCODE_TO_XML_YANG_PUT_ID; + +/** + * Unit test cases for identifier validation test. + */ +public class IdentifierValidationTest { + + private Map<String, String> p; + + private RestconfApiCallNode restconf; + + private RestapiCallNode restApi; + + private DfCaptor dfCaptor; + + /** + * Sets up the pre-requisite for each test case. + * + * @throws SvcLogicException when test case fails + */ + @Before + public void setUp() throws SvcLogicException { + p = new HashMap<>(); + p.put("restapiUser", "user1"); + p.put("restapiPassword", "abc123"); + p.put("responsePrefix", "response"); + p.put("skipSending", "true"); + restApi = new RestapiCallNode(); + restconf = mock(RestconfApiCallNode.class); + dfCaptor = new DfCaptor(); + createMethodMocks(); + } + + /** + * Creates method mocks using mockito for RestconfApiCallNode class. + * + * @throws SvcLogicException when test case fails + */ + private void createMethodMocks() throws SvcLogicException { + doReturn(restApi).when(restconf).getRestapiCallNode(); + doCallRealMethod().when(restconf).sendRequest( + any(Map.class), any(SvcLogicContext.class)); + doCallRealMethod().when(restconf).sendRequest( + any(Map.class), any(SvcLogicContext.class), any(Integer.class)); + doAnswer(dfCaptor).when(restconf).serializeRequest( + any(Map.class), any(YangParameters.class), any(String.class), + any(InstanceIdentifierContext.class)); + doAnswer(dfCaptor).when(restconf).updateReq( + any(String.class), any(YangParameters.class), + any(InstanceIdentifierContext.class)); + } + + /** + * Creates mock using mockito with input data for decoding. + * + * @param decodeData input data + * @throws SvcLogicException when test case fails + */ + private void createMockForDecode(String decodeData) + throws SvcLogicException { + doReturn(decodeData).when(restconf).getResponse( + any(SvcLogicContext.class), any(YangParameters.class), + any(String.class), any(HttpResponse.class)); + doCallRealMethod().when(restconf).serializeResponse( + any(YangParameters.class), any(String.class), any(String.class), + any(InstanceIdentifierContext.class)); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYang() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_" + + "of_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operati" + + "ons/test_name_of_the_module:name_of_the_cont1"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_ID)); + } + + /** + * Verifies encoding of parameters with augment in the URL. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYangWithAugUrl() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_" + + "of_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operati" + + "ons/test_name_of_the_module:name_of_the_cont1/name_of_t" + + "he_cont2/name_of_the_cont4/test_augment_1_for_module:na" + + "me_of_the_cont5"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_WITH_AUG_PATH)); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment for put operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYangWithPut() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_of_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test_name_of_the_module:name_of" + + "_the_cont1/name_of_the_cont2/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_PUT_ID)); + } + + /** + * Verifies encoding of parameters to JSON data format with containers, + * grouping and augment for patch operation-type. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonYangWithPatch() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_o" + + "f_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "patch"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test_name_of_the_module:name_of_" + + "the_cont1/name_of_the_cont2/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_PUT_ID)); + } + + /** + * Verifies encoding of parameters to JSON data format with augment as + * root child. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToJsonWithAugAsRootChild() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_of_" + + "the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test_name_of_the_module:name_of_" + + "the_cont1/name_of_the_cont2/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_YANG_AUG_POST_ID)); + } + + /** + * Verifies decoding of parameters from JSON data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToJsonYang() throws SvcLogicException { + createMockForDecode(ENCODE_TO_JSON_YANG_ID); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "test_name_of_the_module_name_of_the_cont1.name_" + + "of_the_cont2."; + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test_name_of_the_module:name" + + "_of_the_cont1"); + restconf.sendRequest(p, ctx); + verifyAttListYang(ctx, pre); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYang() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_of" + + "_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operations/" + + "test_name_of_the_module:name_of_the_cont1"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_ID)); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment for put operation-type + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYangWithPut() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_" + + "of_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operations/" + + "test_name_of_the_module:name_of_the_cont1/name_of_the_cont2" + + "/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_PUT_ID)); + } + + /** + * Verifies encoding of parameters to XML data format with containers, + * grouping and augment for patch operation-type + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlYangWithPatch() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_of" + + "_the_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "put"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operation" + + "s/test_name_of_the_module:name_of_the_cont1/name_of_the_c" + + "ont2/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_PUT_ID)); + } + + /** + * Verifies encoding of parameters to XML data format with augment as + * root child. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void encodeToXmlWithAugAsRootChild() throws SvcLogicException { + String pre = "test_name_of_the_module_name_of_the_cont1.name_of_the" + + "_cont2."; + SvcLogicContext ctx = createAttListYang(pre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operations/" + + "test_name_of_the_module:name_of_the_cont1/name_of_the_cont2" + + "/name_of_the_cont4"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_YANG_AUG_POST_ID)); + } + + /** + * Verifies decoding of parameters from XML data format with containers, + * grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void decodeToXmlYang() throws SvcLogicException { + createMockForDecode(ENCODE_TO_XML_YANG_ID); + SvcLogicContext ctx = new SvcLogicContext(); + String pre = "test_name_of_the_module_name_of_the_cont1.name_of_" + + "the_cont2."; + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "get"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operation" + + "s/test_name_of_the_module:name_of_the_cont1"); + restconf.sendRequest(p, ctx); + verifyAttListYang(ctx, pre); + } + + /** + * Verifies encoding of and decoding from, JSON respectively for data + * format with containers, grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void codecToJsonRpc() throws SvcLogicException { + createMockForDecode(DECODE_FROM_JSON_RPC_ID); + String inPre = "test_name_of_the_module_name_of_the_create-sfc.input."; + String outPre = "test_name_of_the_module_name_of_the_create-sfc" + + ".output."; + SvcLogicContext ctx = createAttListRpc(inPre); + p.put("dirPath", "src/test/resources"); + p.put("format", "json"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman.com/restconf/operations" + + "/test_name_of_the_module:name_of_the_create-sfc"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_JSON_RPC_ID)); + verifyAttListRpc(ctx, outPre); + } + + /** + * Verifies encoding of and decoding from, XML respectively for data + * format with containers, grouping and augment. + * + * @throws SvcLogicException when test case fails + */ + @Test + public void codecToXmlRpc() throws SvcLogicException { + createMockForDecode(DECODE_FROM_XML_RPC_ID); + String inPre = "test_name_of_the_module_name_of_the_create-sfc.input."; + String outPre = "test_name_of_the_module_name_of_the_create-sfc.output."; + SvcLogicContext ctx = createAttListRpc(inPre); + p.put("dirPath", "src/test/resources"); + p.put("format", "xml"); + p.put("httpMethod", "post"); + p.put("restapiUrl", "http://echo.getpostman" + + ".com/restconf/operations/test_name_of_the_module" + + ":name_of_the_create-sfc"); + restconf.sendRequest(p, ctx); + assertThat(dfCaptor.getResult(), is(ENCODE_TO_XML_RPC_ID)); + verifyAttListRpc(ctx, outPre); + } + + /** + * Creates attribute list for encoding JSON or XML with container, + * grouping and augmented YANG file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAttListYang(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute(pre + "name_of_the_cont3.name_of_the_leaf" + + "10", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_leaf1" + + "", "true"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_leaf2" + + "", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_leaf3" + + "", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "ll1[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "ll1[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "ll2[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "ll2[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "cont4.name_of_the_leaf11", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "list4[0].name_of_the_leaf8", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "list4[1].name_of_the_leaf8", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "list5[0].name_of_the_leaf9", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[0].name_of_the_" + + "list5[1].name_of_the_leaf9", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "leaf1", "true"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "leaf2", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "leaf3", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "ll1[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "ll1[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "ll2[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "ll2[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "cont4.name_of_the_leaf11", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "list4[0].name_of_the_leaf8", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "list4[1].name_of_the_leaf8", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "list5[0].name_of_the_leaf9", "abc"); + ctx.setAttribute(pre + "name_of_the_list1[1].name_of_the_" + + "list5[1].name_of_the_leaf9", "abc"); + ctx.setAttribute(pre + "name_of_the_list2[0].name_of_the_" + + "leaf4", "abc"); + ctx.setAttribute(pre + "name_of_the_list2[1].name_of_the_" + + "leaf4", "abc"); + ctx.setAttribute(pre + "name_of_the_leaf5", "abc"); + ctx.setAttribute(pre + "name_of_the_leaf6", "abc"); + ctx.setAttribute(pre + "name_of_the_ll3[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_ll3[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_ll4[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_ll4[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.name_of_the_leaf10", + "abc"); + ctx.setAttribute(pre + "name_of_the_list6[0].name_of_the_leaf11", + "abc"); + ctx.setAttribute(pre + "name_of_the_list6[1].name_of_the_leaf11", + "abc"); + ctx.setAttribute(pre + "name_of_the_leaf12", "abc"); + ctx.setAttribute(pre + "name_of_the_ll5[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_ll5[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_cont5.name_of_the_leaf13", + "true"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_list7[0].name_of_the" + + "_leaf14", "test"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_list7[1].name_of_the" + + "_leaf14", "create"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_leaf15", "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_ll6[0]", + "unbounded"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_ll6[1]", "8"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_cont13.name_of_the_" + + "cont12.name_of_the_leaf26", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for_" + + "module_name_of_the_cont13.name_of_the_" + + "list9[0].name_of_the_leaf27", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_cont13.name_of_the_" + + "list9[1].name_of_the_leaf27", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_cont13.name_of_the_" + + "leaf28", "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_cont13.name_of_the_" + + "ll9[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_cont4.test_augment_1_for" + + "_module_name_of_the_cont13.name_of_the_" + + "ll9[1]", "abc"); + return ctx; + } + + /** + * Creates attribute list for encoding JSON or XML with RPC YANG file. + * + * @param pre prefix + * @return service logic context + */ + private SvcLogicContext createAttListRpc(String pre) { + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute(pre + "name_of_the_cont14.name_of_the_leaf28", + "abc"); + ctx.setAttribute(pre + "name_of_the_list10[0].name_of_the_leaf29", + "abc"); + ctx.setAttribute(pre + "name_of_the_list10[1].name_of_the_leaf29", + "abc"); + ctx.setAttribute(pre + "name_of_the_leaf30", "abc"); + ctx.setAttribute(pre + "name_of_the_ll10[0]", "abc"); + ctx.setAttribute(pre + "name_of_the_ll10[1]", "abc"); + ctx.setAttribute(pre + "name_of_the_cont15.name_of_the_leaf31", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont13.name_of_the_list9[0]" + + ".name_of_the_leaf27", "abc"); + ctx.setAttribute(pre + "name_of_the_cont13.name_of_the_list9[1]" + + ".name_of_the_leaf27", "abc"); + ctx.setAttribute(pre + "name_of_the_cont13.name_of_the_leaf28", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont13.name_of_the_ll9[0]", + "abc"); + ctx.setAttribute(pre + "name_of_the_cont13.name_of_the_ll9[1]", + "abc"); + return ctx; + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * container, grouping and augmented file. + * + * @param ctx service logic context + * @param pre prefix + */ + private void verifyAttListYang(SvcLogicContext ctx, String pre) { + assertThat(ctx.getAttribute(pre + "name_of_the_cont3.name_of" + + "_the_leaf10"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_leaf1"), is("true")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_leaf2"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_leaf3"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_ll1[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_ll1[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_ll2[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_ll2[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name" + + "_of_the_cont4.name_of_the_leaf11"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name_of" + + "_the_list4[0].name_of_the_leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name_of" + + "_the_list4[1].name_of_the_leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name_of" + + "_the_list5[0].name_of_the_leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[0].name_of" + + "_the_list5[1].name_of_the_leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_leaf1"), is("true")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_leaf2"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_leaf3"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_ll1[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_ll1[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_ll2[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_ll2[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_cont4.name_of_the_leaf11"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_list4[0].name_of_the_leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_list4[1].name_of_the_leaf8"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_list5[0].name_of_the_leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list1[1].name_of" + + "_the_list5[1].name_of_the_leaf9"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list2[0].name_of" + + "_the_leaf4"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list2[1].name_of" + + "_the_leaf4"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_leaf5"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_leaf6"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll3[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll3[1]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll4[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll4[1]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.name_of" + + "_the_leaf10"), is( "abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list6[0].name_of" + + "_the_leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list6[1].name_of" + + "_the_leaf11"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_leaf12"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll5[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll5[1]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont5.name_of_the_leaf13"), + is("true")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_list7[0].name_of_the_leaf14"), + is("test")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_list7[1].name_of_the_leaf14"), + is("create")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_leaf15"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_ll6[0]"), + is("unbounded")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_ll6[1]"), + is("8")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13" + + ".name_of_the_cont12.name_of_" + + "the_leaf26"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13.name_of_the_list9[0]" + + ".name_of_the_leaf27"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13.name_of_the_list9[1]" + + ".name_of_the_leaf27"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13.name_of_the_leaf28"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13.name_of_the_ll9[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont4.test_" + + "augment_1_for_module_name_of_" + + "the_cont13.name_of_the_ll9[1]"), + is("abc")); + } + + /** + * Verifies the attribute list for decoding from JSON or XML with + * RPC YANG file. + * + * @param ctx service logic context + * @param pre prefix + */ + private void verifyAttListRpc(SvcLogicContext ctx, String pre) { + assertThat(ctx.getAttribute(pre + "name_of_the_cont16.name_of_" + + "the_leaf32"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list11[0].name" + + "_of_the_leaf33"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_list11[1].name" + + "_of_the_leaf33"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_leaf34"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll11[0]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_ll11[1]"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont17.name_of_" + + "the_leaf35"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_cont12.name_of_the_leaf26"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_list9[0].name_of_the_leaf27"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_list9[1].name_of_the_leaf27"), + is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_ll9[0]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_ll9[1]"), is("abc")); + assertThat(ctx.getAttribute(pre + "name_of_the_cont13.name_of_" + + "the_leaf28"), is("abc")); + } + + /** + * Captures the data format messages by mocking it, which can be used in + * testing the value. + * + * @param <String> capturing data format + */ + public class DfCaptor<String> implements Answer { + + private String result; + + /** + * Returns the captured data format message. + * + * @return data format message. + */ + public String getResult() { + return result; + } + + @Override + public String answer(InvocationOnMock invocationOnMock) + throws Throwable { + result = (String) invocationOnMock.callRealMethod(); + return result; + } + } + +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationUtilsTest.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationUtilsTest.java new file mode 100644 index 000000000..a866f1c4a --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/dfserializer/IdentifierValidationUtilsTest.java @@ -0,0 +1,469 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2019 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.dfserializer; + +import static org.onap.ccsdk.sli.plugins.yangserializers.dfserializer.DataFormatUtilsTest.addSpace; + +/** + * Unit test case utilities for identifier validation and restconf api + * call node. + */ +public final class IdentifierValidationUtilsTest { + + static final String ENCODE_TO_JSON_YANG_COMMON_ID = "\n " + + "\"test_augment_1_for_module:name_of_the_ll6\": [\n" + + " \"unbounded\",\n" + + " \"8\"\n" + + " ],\n" + + " \"test_augment_1_for_module:name_of_the_cont13\": {\n" + + " \"name_of_the_cont12\": {\n" + + " \"name_of_the_leaf26\": \"abc\"\n" + + " },\n" + + " \"name_of_the_ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_leaf28\": \"abc\",\n" + + " \"name_of_the_list9\": [\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"test_augment_1_for_module:name_of_the_list7\": [\n" + + " {\n" + + " \"name_of_the_leaf14\": \"test\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf14\": \"create\"\n" + + " }\n" + + " ],\n" + + " \"test_augment_1_for_module:name_of_the_leaf15\": \"abc\",\n" + + " \"test_augment_1_for_module:name_of_the_cont5\": {\n" + + " \"name_of_the_leaf13\": \"true\"\n" + + " }"; + + static final String ENCODE_TO_JSON_YANG_AUG_POST_ID = "{\n" + + " \"test_name_of_the_module:name_of_the_leaf10\": \"abc\"," + + ENCODE_TO_JSON_YANG_COMMON_ID + "\n}"; + + static final String ENCODE_TO_JSON_YANG_ID = "{\n" + + " \"test_name_of_the_module:name_of_the_cont2\": {\n" + + " \"name_of_the_ll4\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_leaf5\": \"abc\",\n" + + " \"name_of_the_list6\": [\n" + + " {\n" + + " \"name_of_the_leaf11\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf11\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_ll5\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_ll3\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_leaf6\": \"abc\",\n" + + " \"name_of_the_cont3\": {\n" + + " \"name_of_the_leaf10\": \"abc\"\n" + + " },\n" + + " \"name_of_the_list2\": [\n" + + " {\n" + + " \"name_of_the_leaf4\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf4\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_list1\": [\n" + + " {\n" + + " \"name_of_the_ll2\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_list5\": [\n" + + " {\n" + + " \"name_of_the_leaf9\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf9\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_list4\": [\n" + + " {\n" + + " \"name_of_the_leaf8\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf8\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_leaf1\": \"true\",\n" + + " \"name_of_the_leaf3\": \"abc\",\n" + + " \"name_of_the_leaf2\": \"abc\",\n" + + " \"name_of_the_cont4\": {\n" + + " \"name_of_the_leaf11\": \"abc\"\n" + + " },\n" + + " \"name_of_the_ll1\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"name_of_the_ll2\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_list5\": [\n" + + " {\n" + + " \"name_of_the_leaf9\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf9\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_list4\": [\n" + + " {\n" + + " \"name_of_the_leaf8\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf8\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_leaf1\": \"true\",\n" + + " \"name_of_the_leaf3\": \"abc\",\n" + + " \"name_of_the_leaf2\": \"abc\",\n" + + " \"name_of_the_cont4\": {\n" + + " \"name_of_the_leaf11\": \"abc\"\n" + + " },\n" + + " \"name_of_the_ll1\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"name_of_the_cont4\": {\n" + + " \"name_of_the_leaf10\": \"abc\"," + + addSpace(ENCODE_TO_JSON_YANG_COMMON_ID,8) + "\n" + + " },\n" + + " \"name_of_the_leaf12\": \"abc\"\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_JSON_WITH_AUG_PATH = "{\n" + + " \"test_augment_1_for_module:name_of_the_leaf13\": \"true\"\n" + + "}"; + + static final String ENCODE_TO_JSON_YANG_PUT_ID = "{\n" + + " \"test_name_of_the_module:name_of_the_cont4\": {" + addSpace( + ENCODE_TO_JSON_YANG_COMMON_ID, 4) + ",\n" + + " \"name_of_the_leaf10\": \"abc\"\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_XML_YANG_COMMON_ID = "\n" + + "<name_of_the_ll6 xmlns=\"urn:opendaylight:params:xml:ns:yang:" + + "test:augment:name\">unbounded</name_of_the_ll6>\n" + + "<name_of_the_ll6 xmlns=\"urn:opendaylight:params:xml:ns:yang:" + + "test:augment:name\">8</name_of_the_ll6>\n" + + "<name_of_the_cont13 xmlns=\"urn:opendaylight:params:xml:ns:ya" + + "ng:test:augment:name\">\n" + + " <name_of_the_cont12>\n" + + " <name_of_the_leaf26>abc</name_of_the_leaf26>\n" + + " </name_of_the_cont12>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_leaf28>abc</name_of_the_leaf28>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + "</name_of_the_cont13>\n" + + "<name_of_the_list7 xmlns=\"urn:opendaylight:params:xml:ns:yan" + + "g:test:augment:name\">\n" + + " <name_of_the_leaf14>test</name_of_the_leaf14>\n" + + "</name_of_the_list7>\n" + + "<name_of_the_list7 xmlns=\"urn:opendaylight:params:xml:ns:yan" + + "g:test:augment:name\">\n" + + " <name_of_the_leaf14>create</name_of_the_leaf14>\n" + + "</name_of_the_list7>\n" + + "<name_of_the_leaf15 xmlns=\"urn:opendaylight:params:xml:ns:ya" + + "ng:test:augment:name\">abc</name_of_the_leaf15>\n" + + "<name_of_the_cont5 xmlns=\"urn:opendaylight:params:xml:ns:yan" + + "g:test:augment:name\">\n" + + " <name_of_the_leaf13>true</name_of_the_leaf13>\n" + + "</name_of_the_cont5>"; + + static final String ENCODE_TO_XML_YANG_AUG_POST_ID = "<?xml version=\"1" + + ".0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<name_of_the_leaf10 xmlns=\"urn:opendaylight:params:xml:ns:yan" + + "g:test:name\">abc</name_of_the_leaf10>" + + ENCODE_TO_XML_YANG_COMMON_ID + "\n"; + + static final String ENCODE_TO_XML_YANG_PUT_ID = "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<name_of_the_cont4 xmlns=\"urn:opendaylight:params:xml:ns:yang" + + ":test:name\">\n" + + " <name_of_the_leaf10>abc</name_of_the_leaf10>" + + addSpace(ENCODE_TO_XML_YANG_COMMON_ID, 4) + "\n</name_of_the_co" + + "nt4>\n"; + + static final String ENCODE_TO_XML_YANG_ID= "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<name_of_the_cont2 xmlns=\"urn:opendaylight:params:xml:ns:yan" + + "g:test:name\">\n" + + " <name_of_the_ll4>abc</name_of_the_ll4>\n" + + " <name_of_the_ll4>abc</name_of_the_ll4>\n" + + " <name_of_the_leaf5>abc</name_of_the_leaf5>\n" + + " <name_of_the_list6>\n" + + " <name_of_the_leaf11>abc</name_of_the_leaf11>\n" + + " </name_of_the_list6>\n" + + " <name_of_the_list6>\n" + + " <name_of_the_leaf11>abc</name_of_the_leaf11>\n" + + " </name_of_the_list6>\n" + + " <name_of_the_ll5>abc</name_of_the_ll5>\n" + + " <name_of_the_ll5>abc</name_of_the_ll5>\n" + + " <name_of_the_ll3>abc</name_of_the_ll3>\n" + + " <name_of_the_ll3>abc</name_of_the_ll3>\n" + + " <name_of_the_leaf6>abc</name_of_the_leaf6>\n" + + " <name_of_the_cont3>\n" + + " <name_of_the_leaf10>abc</name_of_the_leaf10>\n" + + " </name_of_the_cont3>\n" + + " <name_of_the_list2>\n" + + " <name_of_the_leaf4>abc</name_of_the_leaf4>\n" + + " </name_of_the_list2>\n" + + " <name_of_the_list2>\n" + + " <name_of_the_leaf4>abc</name_of_the_leaf4>\n" + + " </name_of_the_list2>\n" + + " <name_of_the_list1>\n" + + " <name_of_the_ll2>abc</name_of_the_ll2>\n" + + " <name_of_the_ll2>abc</name_of_the_ll2>\n" + + " <name_of_the_list5>\n" + + " <name_of_the_leaf9>abc</name_of_the_leaf9>\n" + + " </name_of_the_list5>\n" + + " <name_of_the_list5>\n" + + " <name_of_the_leaf9>abc</name_of_the_leaf9>\n" + + " </name_of_the_list5>\n" + + " <name_of_the_list4>\n" + + " <name_of_the_leaf8>abc</name_of_the_leaf8>\n" + + " </name_of_the_list4>\n" + + " <name_of_the_list4>\n" + + " <name_of_the_leaf8>abc</name_of_the_leaf8>\n" + + " </name_of_the_list4>\n" + + " <name_of_the_leaf1>true</name_of_the_leaf1>\n" + + " <name_of_the_leaf3>abc</name_of_the_leaf3>\n" + + " <name_of_the_leaf2>abc</name_of_the_leaf2>\n" + + " <name_of_the_cont4>\n" + + " <name_of_the_leaf11>abc</name_of_the_leaf11>\n" + + " </name_of_the_cont4>\n" + + " <name_of_the_ll1>abc</name_of_the_ll1>\n" + + " <name_of_the_ll1>abc</name_of_the_ll1>\n" + + " </name_of_the_list1>\n" + + " <name_of_the_list1>\n" + + " <name_of_the_ll2>abc</name_of_the_ll2>\n" + + " <name_of_the_ll2>abc</name_of_the_ll2>\n" + + " <name_of_the_list5>\n" + + " <name_of_the_leaf9>abc</name_of_the_leaf9>\n" + + " </name_of_the_list5>\n" + + " <name_of_the_list5>\n" + + " <name_of_the_leaf9>abc</name_of_the_leaf9>\n" + + " </name_of_the_list5>\n" + + " <name_of_the_list4>\n" + + " <name_of_the_leaf8>abc</name_of_the_leaf8>\n" + + " </name_of_the_list4>\n" + + " <name_of_the_list4>\n" + + " <name_of_the_leaf8>abc</name_of_the_leaf8>\n" + + " </name_of_the_list4>\n" + + " <name_of_the_leaf1>true</name_of_the_leaf1>\n" + + " <name_of_the_leaf3>abc</name_of_the_leaf3>\n" + + " <name_of_the_leaf2>abc</name_of_the_leaf2>\n" + + " <name_of_the_cont4>\n" + + " <name_of_the_leaf11>abc</name_of_the_leaf11>\n" + + " </name_of_the_cont4>\n" + + " <name_of_the_ll1>abc</name_of_the_ll1>\n" + + " <name_of_the_ll1>abc</name_of_the_ll1>\n" + + " </name_of_the_list1>\n" + + " <name_of_the_cont4>\n" + + " <name_of_the_leaf10>abc</name_of_the_leaf10>" + + addSpace(ENCODE_TO_XML_YANG_COMMON_ID, 8) + "\n" + + " </name_of_the_cont4>\n" + + " <name_of_the_leaf12>abc</name_of_the_leaf12>\n" + + "</name_of_the_cont2>\n"; + + static final String ENCODE_TO_JSON_RPC_ID = "{\n" + + " \"test_name_of_the_module:input\": {\n" + + " \"name_of_the_cont14\": {\n" + + " \"name_of_the_leaf28\": \"abc\"\n" + + " },\n" + + " \"name_of_the_cont13\": {\n" + + " \"name_of_the_ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_leaf28\": \"abc\",\n" + + " \"name_of_the_list9\": [\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " \"name_of_the_leaf30\": \"abc\",\n" + + " \"name_of_the_ll10\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_list10\": [\n" + + " {\n" + + " \"name_of_the_leaf29\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf29\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_cont15\": {\n" + + " \"name_of_the_leaf31\": \"abc\"\n" + + " }\n" + + " }\n" + + "}"; + + static final String DECODE_FROM_JSON_RPC_ID = "{\n" + + " \"test_name_of_the_module:output\": {\n" + + " \"name_of_the_cont16\": {\n" + + " \"name_of_the_leaf32\": \"abc\"\n" + + " },\n" + + " \"name_of_the_list11\": [\n" + + " {\n" + + " \"name_of_the_leaf33\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf33\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_leaf34\": \"abc\",\n" + + " \"name_of_the_ll11\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_cont17\": {\n" + + " \"name_of_the_leaf35\": \"abc\"\n" + + " },\n" + + " \"name_of_the_cont13\": {\n" + + " \"name_of_the_cont12\": {\n" + + " \"name_of_the_leaf26\": \"abc\"\n" + + " },\n" + + " \"name_of_the_list9\": [\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " },\n" + + " {\n" + + " \"name_of_the_leaf27\": \"abc\"\n" + + " }\n" + + " ],\n" + + " \"name_of_the_ll9\": [\n" + + " \"abc\",\n" + + " \"abc\"\n" + + " ],\n" + + " \"name_of_the_leaf28\": \"abc\"\n" + + " }\n" + + " }\n" + + "}"; + + static final String ENCODE_TO_XML_RPC_ID = "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<input xmlns=\"urn:opendaylight:params:xml:ns:yang:test:name\"" + + ">\n" + + " <name_of_the_cont14>\n" + + " <name_of_the_leaf28>abc</name_of_the_leaf28>\n" + + " </name_of_the_cont14>\n" + + " <name_of_the_cont13>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_leaf28>abc</name_of_the_leaf28>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + " </name_of_the_cont13>\n" + + " <name_of_the_leaf30>abc</name_of_the_leaf30>\n" + + " <name_of_the_ll10>abc</name_of_the_ll10>\n" + + " <name_of_the_ll10>abc</name_of_the_ll10>\n" + + " <name_of_the_list10>\n" + + " <name_of_the_leaf29>abc</name_of_the_leaf29>\n" + + " </name_of_the_list10>\n" + + " <name_of_the_list10>\n" + + " <name_of_the_leaf29>abc</name_of_the_leaf29>\n" + + " </name_of_the_list10>\n" + + " <name_of_the_cont15>\n" + + " <name_of_the_leaf31>abc</name_of_the_leaf31>\n" + + " </name_of_the_cont15>\n" + + "</input>\n"; + + static final String DECODE_FROM_XML_RPC_ID = "<?xml version=\"1.0\" " + + "encoding=\"UTF-8\" standalone=\"no\"?>\n" + + "<output xmlns=\"urn:opendaylight:params:xml:ns:yang:test:name" + + "\">\n" + + " <name_of_the_cont16>\n" + + " <name_of_the_leaf32>abc</name_of_the_leaf32>\n" + + " </name_of_the_cont16>\n" + + " <name_of_the_list11>\n" + + " <name_of_the_leaf33>abc</name_of_the_leaf33>\n" + + " </name_of_the_list11>\n" + + " <name_of_the_list11>\n" + + " <name_of_the_leaf33>abc</name_of_the_leaf33>\n" + + " </name_of_the_list11>\n" + + " <name_of_the_leaf34>abc</name_of_the_leaf34>\n" + + " <name_of_the_ll11>abc</name_of_the_ll11>\n" + + " <name_of_the_ll11>abc</name_of_the_ll11>\n" + + " <name_of_the_cont17>\n" + + " <name_of_the_leaf35>abc</name_of_the_leaf35>\n" + + " </name_of_the_cont17>\n" + + " <name_of_the_cont13>\n" + + " <name_of_the_cont12>\n" + + " <name_of_the_leaf26>abc</name_of_the_leaf26>\n" + + " </name_of_the_cont12>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + " <name_of_the_list9>\n" + + " <name_of_the_leaf27>abc</name_of_the_leaf27>\n" + + " </name_of_the_list9>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_ll9>abc</name_of_the_ll9>\n" + + " <name_of_the_leaf28>abc</name_of_the_leaf28>\n" + + " </name_of_the_cont13>\n" + + "</output>"; +} diff --git a/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesSerializerTest.java b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesSerializerTest.java new file mode 100644 index 000000000..c3a6b4ea8 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/java/org/onap/ccsdk/sli/plugins/yangserializers/pnserializer/PropertiesSerializerTest.java @@ -0,0 +1,1144 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP - CCSDK + * ================================================================================ + * Copyright (C) 2018 Huawei Technologies Co., Ltd. All rights reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.yangserializers.pnserializer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertTrue; + +public final class PropertiesSerializerTest { + private SchemaContext context; + + @Before + public void initialization() throws FileNotFoundException { + context = compileYangFile(); + } + + @Test + public void testBasicConstructs() throws SvcLogicException { + String uri = "test-yang:cont1/cont2"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont1.cont2.cont3.leaf10", "abc"); + params.put("test-yang_cont1.cont2.list1[0].leaf1", "abc"); + params.put("test-yang_cont1.cont2.list1[0].leaf2", "abc"); + params.put("test-yang_cont1.cont2.list1[0].leaf3", "abc"); + params.put("test-yang_cont1.cont2.list1[0].ll1[0]", "abc"); + params.put("test-yang_cont1.cont2.list1[0].ll1[1]", "abc"); + params.put("test-yang_cont1.cont2.list1[0].ll2[0]", "abc"); + params.put("test-yang_cont1.cont2.list1[0].ll2[1]", "abc"); + params.put("test-yang_cont1.cont2.list1[0].cont4.leaf11", "abc"); + params.put("test-yang_cont1.cont2.list1[0].list4[0].leaf8", "abc"); + params.put("test-yang_cont1.cont2.list1[0].list4[1].leaf8", "abc"); + params.put("test-yang_cont1.cont2.list1[0].list5[0].leaf9", "abc"); + params.put("test-yang_cont1.cont2.list1[0].list5[1].leaf9", "abc"); + params.put("test-yang_cont1.cont2.list1[1].leaf1", "abc"); + params.put("test-yang_cont1.cont2.list1[1].leaf2", "abc"); + params.put("test-yang_cont1.cont2.list1[1].leaf3", "abc"); + params.put("test-yang_cont1.cont2.list1[1].ll1[0]", "abc"); + params.put("test-yang_cont1.cont2.list1[1].ll1[1]", "abc"); + params.put("test-yang_cont1.cont2.list1[1].ll2[0]", "abc"); + params.put("test-yang_cont1.cont2.list1[1].ll2[1]", "abc"); + params.put("test-yang_cont1.cont2.list1[1].cont4.leaf11", "abc"); + params.put("test-yang_cont1.cont2.list1[1].list4[0].leaf8", "abc"); + params.put("test-yang_cont1.cont2.list1[1].list4[1].leaf8", "abc"); + params.put("test-yang_cont1.cont2.list1[1].list5[0].leaf9", "abc"); + params.put("test-yang_cont1.cont2.list1[1].list5[1].leaf9", "abc"); + params.put("test-yang_cont1.cont2.list2[0].leaf4", "abc"); + params.put("test-yang_cont1.cont2.list2[1].leaf4", "abc"); + params.put("test-yang_cont1.cont2.leaf5", "abc"); + params.put("test-yang_cont1.cont2.leaf6", "abc"); + params.put("test-yang_cont1.cont2.ll3[0]", "abc"); + params.put("test-yang_cont1.cont2.ll3[1]", "abc"); + params.put("test-yang_cont1.cont2.ll4[0]", "abc"); + params.put("test-yang_cont1.cont2.ll4[1]", "abc"); + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont3"), is(true)); + SingleInstanceNode cont3 = ((SingleInstanceNode) childNodes.get("cont3")); + assertThat(cont3.uri(), is("test-yang:cont1.cont2.cont3")); + assertThat(cont3.children().containsKey("leaf10"), is(true)); + + assertThat(childNodes.containsKey("list1"), is(true)); + HolderNode list1Holder = ((ListHolderNode) childNodes.get("list1")); + assertThat(list1Holder.uri(), is("test-yang:cont1.cont2.list1")); + MultiInstanceNode list10 = ((MultiInstanceNode) list1Holder.child("0")); + assertThat(list10.uri(), is("test-yang:cont1.cont2.list1[0]")); + Map<String, DataNodeChild> list10Child = list10.children(); + assertThat(list10Child.containsKey("leaf1"), is(true)); + LeafNode l = ((LeafNode) list10Child.get("leaf1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].leaf1")); + assertThat(list10Child.containsKey("leaf2"), is(true)); + l = ((LeafNode) list10Child.get("leaf2")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].leaf2")); + assertThat(list10Child.containsKey("leaf2"), is(true)); + l = ((LeafNode) list10Child.get("leaf3")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].leaf3")); + + LeafListHolderNode ll1Holder = ((LeafListHolderNode) list10Child.get("ll1")); + assertThat(ll1Holder.uri(), is("test-yang:cont1.cont2.list1[0].ll1")); + assertThat(ll1Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll1Holder.child("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].ll1[0]")); + assertThat(ll1Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll1Holder.child("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].ll1[1]")); + + LeafListHolderNode ll2Holder = ((LeafListHolderNode) list10Child.get("ll2")); + assertThat(ll2Holder.uri(), is("test-yang:cont1.cont2.list1[0].ll2")); + assertThat(ll2Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll2Holder.child("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].ll2[0]")); + assertThat(ll2Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll2Holder.child("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].ll2[1]")); + + SingleInstanceNode cont4 = ((SingleInstanceNode) list10Child.get("cont4")); + assertThat(cont4.uri(), is("test-yang:cont1.cont2.list1[0].cont4")); + assertThat(cont4.children().containsKey("leaf11"), is(true)); + l = ((LeafNode) cont4.children().get("leaf11")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].cont4.leaf11")); + + HolderNode list4Holder = ((HolderNode) list10Child.get("list4")); + assertThat(list4Holder.uri(), is("test-yang:cont1.cont2.list1[0].list4")); + Map<String, PropertiesNode> c = list4Holder.children(); + MultiInstanceNode list40 = ((MultiInstanceNode) c.get("0")); + assertThat(list40.uri(), is("test-yang:cont1.cont2.list1[0].list4[0]")); + assertThat(list40.children().containsKey("leaf8"), is(true)); + l = ((LeafNode) list40.children().get("leaf8")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].list4[0].leaf8")); + MultiInstanceNode list41 = ((MultiInstanceNode) c.get("1")); + assertThat(list41.uri(), is("test-yang:cont1.cont2.list1[0].list4[1]")); + assertThat(list41.children().containsKey("leaf8"), is(true)); + l = ((LeafNode) list41.children().get("leaf8")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].list4[1].leaf8")); + + HolderNode list5Holder = ((HolderNode) list10Child.get("list5")); + assertThat(list5Holder.uri(), is("test-yang:cont1.cont2.list1[0].list5")); + c = list5Holder.children(); + MultiInstanceNode list50 = ((MultiInstanceNode) c.get("0")); + assertThat(list50.uri(), is("test-yang:cont1.cont2.list1[0].list5[0]")); + assertThat(list50.children().containsKey("leaf9"), is(true)); + l = ((LeafNode) list50.children().get("leaf9")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].list5[0].leaf9")); + MultiInstanceNode list51 = ((MultiInstanceNode) c.get("1")); + assertThat(list51.uri(), is("test-yang:cont1.cont2.list1[0].list5[1]")); + assertThat(list51.children().containsKey("leaf9"), is(true)); + l = ((LeafNode) list51.children().get("leaf9")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[0].list5[1].leaf9")); + + MultiInstanceNode list11 = ((MultiInstanceNode) list1Holder.child("1")); + assertThat(list11.uri(), is("test-yang:cont1.cont2.list1[1]")); + Map<String, DataNodeChild> list11Child = list11.children(); + assertThat(list11Child.containsKey("leaf1"), is(true)); + l = ((LeafNode) list11Child.get("leaf1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].leaf1")); + assertThat(list11Child.containsKey("leaf2"), is(true)); + l = ((LeafNode) list11Child.get("leaf2")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].leaf2")); + assertThat(list11Child.containsKey("leaf3"), is(true)); + l = ((LeafNode) list11Child.get("leaf3")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].leaf3")); + + ll1Holder = ((LeafListHolderNode) list11Child.get("ll1")); + assertThat(ll1Holder.uri(), is("test-yang:cont1.cont2.list1[1].ll1")); + assertThat(ll1Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll1Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].ll1[0]")); + assertThat(ll1Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll1Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].ll1[1]")); + + ll2Holder = ((LeafListHolderNode) list11Child.get("ll2")); + assertThat(ll2Holder.uri(), is("test-yang:cont1.cont2.list1[1].ll2")); + assertThat(ll2Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll2Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].ll2[0]")); + assertThat(ll2Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll2Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].ll2[1]")); + + cont4 = ((SingleInstanceNode) list11Child.get("cont4")); + assertThat(cont4.uri(), is("test-yang:cont1.cont2.list1[1].cont4")); + assertThat(cont4.children().containsKey("leaf11"), is(true)); + l = ((LeafNode) cont4.children().get("leaf11")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].cont4.leaf11")); + + list4Holder = ((HolderNode) list11Child.get("list4")); + assertThat(list4Holder.uri(), is("test-yang:cont1.cont2.list1[1].list4")); + c = list4Holder.children(); + list40 = ((MultiInstanceNode) c.get("0")); + assertThat(list40.uri(), is("test-yang:cont1.cont2.list1[1].list4[0]")); + assertThat(list40.children().containsKey("leaf8"), is(true)); + l = ((LeafNode) list40.children().get("leaf8")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].list4[0].leaf8")); + list41 = ((MultiInstanceNode) c.get("1")); + assertThat(list41.uri(), is("test-yang:cont1.cont2.list1[1].list4[1]")); + assertThat(list41.children().containsKey("leaf8"), is(true)); + l = ((LeafNode) list41.children().get("leaf8")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].list4[1].leaf8")); + + list5Holder = ((HolderNode) list11Child.get("list5")); + assertThat(list5Holder.uri(), is("test-yang:cont1.cont2.list1[1].list5")); + c = list5Holder.children(); + list50 = ((MultiInstanceNode) c.get("0")); + assertThat(list50.uri(), is("test-yang:cont1.cont2.list1[1].list5[0]")); + assertThat(list50.children().containsKey("leaf9"), is(true)); + l = ((LeafNode) list50.children().get("leaf9")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].list5[0].leaf9")); + list51 = ((MultiInstanceNode) c.get("1")); + assertThat(list51.uri(), is("test-yang:cont1.cont2.list1[1].list5[1]")); + assertThat(list51.children().containsKey("leaf9"), is(true)); + l = ((LeafNode) list51.children().get("leaf9")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list1[1].list5[1].leaf9")); + + assertThat(childNodes.containsKey("list2"), is(true)); + HolderNode list2Holder = ((HolderNode) childNodes.get("list2")); + assertThat(list2Holder.uri(), is("test-yang:cont1.cont2.list2")); + InnerNode list20 = ((InnerNode) list2Holder.children().get("0")); + assertThat(list20.uri(), is("test-yang:cont1.cont2.list2[0]")); + assertThat(list20.children().containsKey("leaf4"), is(true)); + l = ((LeafNode) list20.children().get("leaf4")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list2[0].leaf4")); + InnerNode list21 = ((InnerNode) list2Holder.children().get("1")); + assertThat(list21.uri(), is("test-yang:cont1.cont2.list2[1]")); + assertThat(list21.children().containsKey("leaf4"), is(true)); + l = ((LeafNode) list21.children().get("leaf4")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list2[1].leaf4")); + + assertThat(childNodes.containsKey("leaf5"), is(true)); + l = ((LeafNode) childNodes.get("leaf5")); + assertThat(l.uri(), is("test-yang:cont1.cont2.leaf5")); + assertThat(childNodes.containsKey("leaf6"), is(true)); + l = ((LeafNode) childNodes.get("leaf6")); + assertThat(l.uri(), is("test-yang:cont1.cont2.leaf6")); + + HolderNode ll3Holder = ((HolderNode) childNodes.get("ll3")); + assertThat(ll3Holder.uri(), is("test-yang:cont1.cont2.ll3")); + assertThat(((LeafNode) ll3Holder.children().get("0")).name(), is("ll3")); + l = ((LeafNode) ll3Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll3[0]")); + assertThat(((LeafNode) ll3Holder.children().get("1")).name(), is("ll3")); + l = ((LeafNode) ll3Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll3[1]")); + + HolderNode ll4Holder = ((HolderNode) childNodes.get("ll4")); + assertThat(ll4Holder.uri(), is("test-yang:cont1.cont2.ll4")); + assertThat(((LeafNode) ll4Holder.children().get("0")).name(), is("ll4")); + l = ((LeafNode) ll4Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll4[0]")); + assertThat(((LeafNode) ll4Holder.children().get("1")).name(), is("ll4")); + l = ((LeafNode) ll4Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll4[1]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testAugment() throws SvcLogicException { + String uri = "test-yang:cont1/cont2"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont1.cont2.cont4.leaf10", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_cont5.leaf13", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_list7[0].leaf14", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_list7[1].leaf14", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_leaf15", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_ll6[0]", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_ll6[1]", "abc"); + params.put("test-yang_cont1.cont2.list6[0].leaf11", "abc"); + params.put("test-yang_cont1.cont2.list6[1].leaf11", "abc"); + params.put("test-yang_cont1.cont2.leaf12", "abc"); + params.put("test-yang_cont1.cont2.ll5[0]", "abc"); + params.put("test-yang_cont1.cont2.ll5[1]", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont4"), is(true)); + SingleInstanceNode cont4 = ((SingleInstanceNode) childNodes.get("cont4")); + for (Map.Entry<Object, Collection<PropertiesNode>> augToChild + : cont4.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> child = augToChild.getValue(); + if (!child.isEmpty()) { + List<String> expectedNodes = new LinkedList<>(); + expectedNodes.add("test-yang:cont1.cont2.cont4.test-augment:cont5"); + expectedNodes.add("test-yang:cont1.cont2.cont4.test-augment:list7"); + expectedNodes.add("test-yang:cont1.cont2.cont4.test-augment:leaf15"); + expectedNodes.add("test-yang:cont1.cont2.cont4.test-augment:ll6"); + assertThat(expectedNodes.size(), is(child.size())); + for (PropertiesNode pNode : child) { + assertThat(expectedNodes.contains(pNode.uri()), is(true)); + if (pNode.uri().equals("test-yang:cont1.cont2.cont4.test-augment:cont5")) { + assertThat(((SingleInstanceNode) pNode).children().containsKey("leaf13"), is(true)); + LeafNode l = ((LeafNode) ((SingleInstanceNode) pNode).children().get("leaf13")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont5.leaf13")); + } else if (pNode.uri().equals("test-yang:cont1.cont2.cont4.test-augment:list7")) { + ListHolderNode list7Holder = ((ListHolderNode) pNode); + MultiInstanceNode list7 = ((MultiInstanceNode) list7Holder.child("0")); + assertThat(list7.uri(), is("test-yang:cont1.cont2.cont4.test-augment:list7[0]")); + Map<String, DataNodeChild> list7Child = list7.children(); + assertThat(list7Child.containsKey("leaf14"), is(true)); + LeafNode l = ((LeafNode) list7Child.get("leaf14")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:list7[0].leaf14")); + list7 = ((MultiInstanceNode) list7Holder.child("1")); + assertThat(list7.uri(), is("test-yang:cont1.cont2.cont4.test-augment:list7[1]")); + list7Child = list7.children(); + assertThat(list7Child.containsKey("leaf14"), is(true)); + l = ((LeafNode) list7Child.get("leaf14")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:list7[1].leaf14")); + } else if (pNode.uri().equals("test-yang:cont1.cont2.cont4.test-augment:leaf15")) { + LeafNode leaf15 = ((LeafNode) pNode); + assertThat(leaf15.name(), is("leaf15")); + assertThat(leaf15.uri(), is("test-yang:cont1.cont2.cont4.test-augment:leaf15")); + } else if (pNode.uri().equals("test-yang:cont1.cont2.cont4.test-augment:ll6")) { + LeafListHolderNode ll6Holder = ((LeafListHolderNode) pNode); + assertThat(ll6Holder.children().containsKey("0"), is(true)); + LeafNode l = ((LeafNode) ll6Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:ll6[0]")); + assertThat(ll6Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll6Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:ll6[1]")); + } + } + } + } + assertThat(cont4.uri(), is("test-yang:cont1.cont2.cont4")); + assertThat(cont4.children().containsKey("leaf10"), is(true)); + LeafNode l = ((LeafNode) cont4.children().get("leaf10")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.leaf10")); + + assertThat(childNodes.containsKey("list6"), is(true)); + HolderNode list6Holder = ((ListHolderNode) childNodes.get("list6")); + assertThat(list6Holder.uri(), is("test-yang:cont1.cont2.list6")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-yang:cont1.cont2.list6[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf11"), is(true)); + l = ((LeafNode) list6Child.get("leaf11")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list6[0].leaf11")); + list6 = ((MultiInstanceNode) list6Holder.child("1")); + assertThat(list6.uri(), is("test-yang:cont1.cont2.list6[1]")); + list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf11"), is(true)); + l = ((LeafNode) list6Child.get("leaf11")); + assertThat(l.uri(), is("test-yang:cont1.cont2.list6[1].leaf11")); + + assertThat(childNodes.containsKey("leaf12"), is(true)); + LeafNode leaf12 = ((LeafNode) childNodes.get("leaf12")); + assertThat(leaf12.name(), is("leaf12")); + assertThat(leaf12.uri(), is("test-yang:cont1.cont2.leaf12")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) childNodes.get("ll5")); + assertThat(ll5Holder.uri(), is("test-yang:cont1.cont2.ll5")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll5[0]")); + assertThat(ll5Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll5Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont1.cont2.ll5[1]")); + + Map<String, String> output = ser.decode(node); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testChoiceCase1() throws SvcLogicException { + String uri = "test-yang:cont8"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont8.cont6.leaf16", "abc"); + params.put("test-yang_cont8.list8[0].leaf18", "abc"); + params.put("test-yang_cont8.list8[1].leaf18", "abc"); + params.put("test-yang_cont8.leaf19", "abc"); + params.put("test-yang_cont8.ll7[0]", "abc"); + params.put("test-yang_cont8.ll7[1]", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont6"), is(true)); + SingleInstanceNode cont6 = ((SingleInstanceNode) childNodes.get("cont6")); + assertThat(cont6.uri(), is("test-yang:cont8.cont6")); + assertThat(cont6.children().containsKey("leaf16"), is(true)); + LeafNode l = ((LeafNode) cont6.children().get("leaf16")); + assertThat(l.uri(), is("test-yang:cont8.cont6.leaf16")); + + assertThat(childNodes.containsKey("list8"), is(true)); + HolderNode list6Holder = ((ListHolderNode) childNodes.get("list8")); + assertThat(list6Holder.uri(), is("test-yang:cont8.list8")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-yang:cont8.list8[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf18"), is(true)); + l = ((LeafNode) list6Child.get("leaf18")); + assertThat(l.uri(), is("test-yang:cont8.list8[0].leaf18")); + list6 = ((MultiInstanceNode) list6Holder.child("1")); + list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf18"), is(true)); + l = ((LeafNode) list6Child.get("leaf18")); + assertThat(l.uri(), is("test-yang:cont8.list8[1].leaf18")); + + assertThat(childNodes.containsKey("leaf19"), is(true)); + LeafNode leaf12 = ((LeafNode) childNodes.get("leaf19")); + assertThat(leaf12.name(), is("leaf19")); + assertThat(leaf12.uri(), is("test-yang:cont8.leaf19")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) childNodes.get("ll7")); + assertThat(ll5Holder.uri(), is("test-yang:cont8.ll7")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont8.ll7[0]")); + assertThat(ll5Holder.children().containsKey("1"), is(true)); + l = ((LeafNode) ll5Holder.children().get("1")); + assertThat(l.uri(), is("test-yang:cont8.ll7[1]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testChoiceCase2() throws SvcLogicException { + String uri = "test-yang:cont9"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont9.leaf20", "abc"); + params.put("test-yang_cont9.ll8[0]", "abc"); + params.put("test-yang_cont9.cont11.leaf25", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont11"), is(true)); + SingleInstanceNode cont4 = ((SingleInstanceNode) childNodes.get("cont11")); + assertThat(cont4.uri(), is("test-yang:cont9.cont11")); + assertThat(cont4.children().containsKey("leaf25"), is(true)); + LeafNode l = ((LeafNode) cont4.children().get("leaf25")); + assertThat(l.uri(), is("test-yang:cont9.cont11.leaf25")); + + assertThat(childNodes.containsKey("leaf20"), is(true)); + l = ((LeafNode) childNodes.get("leaf20")); + assertThat(l.uri(), is("test-yang:cont9.leaf20")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) childNodes.get("ll8")); + assertThat(ll5Holder.uri(), is("test-yang:cont9.ll8")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont9.ll8[0]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testChoiceCase3() throws SvcLogicException { + String uri = "test-yang:cont8/cont6"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont8.cont6.test-augment_leaf21", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + for (Map.Entry<Object, Collection<PropertiesNode>> augToChild + : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> child = augToChild.getValue(); + if (!child.isEmpty()) { + List<String> expectedNodes = new LinkedList<>(); + expectedNodes.add("test-yang:cont8.cont6.test-augment:leaf21"); + assertThat(expectedNodes.size(), is(child.size())); + for (PropertiesNode pNode : child) { + assertThat(expectedNodes.contains(pNode.uri()), is(true)); + } + } + } + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("leaf21"), is(true)); + LeafNode leaf12 = ((LeafNode) childNodes.get("leaf21")); + assertThat(leaf12.name(), is("leaf21")); + assertThat(leaf12.uri(), is("test-yang:cont8.cont6.test-augment:leaf21")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testGrouping() throws SvcLogicException { + String uri = "test-yang:cont13"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont13.cont12.leaf26", "abc"); + params.put("test-yang_cont13.list9[0].leaf27", "abc"); + params.put("test-yang_cont13.leaf28", "abc"); + params.put("test-yang_cont13.ll9[0]", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont12"), is(true)); + SingleInstanceNode cont4 = ((SingleInstanceNode) childNodes.get("cont12")); + assertThat(cont4.uri(), is("test-yang:cont13.cont12")); + assertThat(cont4.children().containsKey("leaf26"), is(true)); + LeafNode l = ((LeafNode) cont4.children().get("leaf26")); + assertThat(l.uri(), is("test-yang:cont13.cont12.leaf26")); + + assertThat(childNodes.containsKey("list9"), is(true)); + HolderNode list6Holder = ((ListHolderNode) childNodes.get("list9")); + assertThat(list6Holder.uri(), is("test-yang:cont13.list9")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-yang:cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-yang:cont13.list9[0].leaf27")); + + assertThat(childNodes.containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) childNodes.get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-yang:cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) childNodes.get("ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testGrouping2() throws SvcLogicException { + String uri = "test-yang:cont9/cont11"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont9.cont11.leaf25", "abc"); + params.put("test-yang_cont9.cont11.cont13.cont12.leaf26", "abc"); + params.put("test-yang_cont9.cont11.cont13.list9[0].leaf27", "abc"); + params.put("test-yang_cont9.cont11.cont13.leaf28", "abc"); + params.put("test-yang_cont9.cont11.cont13.ll9[0]", "abc"); + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont13"), is(true)); + SingleInstanceNode cont13 = ((SingleInstanceNode) childNodes.get("cont13")); + assertThat(cont13.uri(), is("test-yang:cont9.cont11.cont13")); + SingleInstanceNode cont12 = ((SingleInstanceNode) cont13.children().get("cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + assertThat(cont12.uri(), is("test-yang:cont9.cont11.cont13.cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + LeafNode l = ((LeafNode) cont12.children().get("leaf26")); + assertThat(l.uri(), is("test-yang:cont9.cont11.cont13.cont12.leaf26")); + + assertThat(cont13.children().containsKey("list9"), is(true)); + HolderNode list6Holder = ((ListHolderNode) cont13.children().get("list9")); + assertThat(list6Holder.uri(), is("test-yang:cont9.cont11.cont13.list9")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-yang:cont9.cont11.cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-yang:cont9.cont11.cont13.list9[0].leaf27")); + + assertThat(cont13.children().containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) cont13.children().get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-yang:cont9.cont11.cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) cont13.children().get("ll9")); + assertThat(ll5Holder.uri(), is("test-yang:cont9.cont11.cont13.ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont9.cont11.cont13.ll9[0]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testGrouping3() throws SvcLogicException { + String uri = "test-augment:cont13"; + Map<String, String> params = new HashMap<>(); + params.put("test-augment_cont13.cont12.leaf26", "abc"); + params.put("test-augment_cont13.list9[0].leaf27", "abc"); + params.put("test-augment_cont13.leaf28", "abc"); + params.put("test-augment_cont13.ll9[0]", "abc"); + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont12"), is(true)); + SingleInstanceNode cont12 = ((SingleInstanceNode) childNodes.get("cont12")); + assertThat(cont12.uri(), is("test-augment:cont13.cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + LeafNode l = ((LeafNode) cont12.children().get("leaf26")); + assertThat(l.uri(), is("test-augment:cont13.cont12.leaf26")); + + assertThat(childNodes.containsKey("list9"), is(true)); + HolderNode list6Holder = ((ListHolderNode) childNodes.get("list9")); + assertThat(list6Holder.uri(), is("test-augment:cont13.list9")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-augment:cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-augment:cont13.list9[0].leaf27")); + + assertThat(childNodes.containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) childNodes.get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-augment:cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) childNodes.get("ll9")); + assertThat(ll5Holder.uri(), is("test-augment:cont13.ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-augment:cont13.ll9[0]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testGrouping4() throws SvcLogicException { + String uri = "test-yang:cont1/cont2/cont4"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont1.cont2.cont4.test-augment_cont13.cont12.leaf26", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_cont13.list9[0].leaf27", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_cont13.leaf28", "abc"); + params.put("test-yang_cont1.cont2.cont4.test-augment_cont13.ll9[0]", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + for (Map.Entry<Object, Collection<PropertiesNode>> augToChild + : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> child = augToChild.getValue(); + if (!child.isEmpty()) { + List<String> expectedNodes = new LinkedList<>(); + expectedNodes.add("test-yang:cont1.cont2.cont4.test-augment:cont13"); + assertThat(expectedNodes.size(), is(child.size())); + for (PropertiesNode pNode : child) { + assertThat(expectedNodes.contains(pNode.uri()), is(true)); + SingleInstanceNode cont13 = ((SingleInstanceNode) pNode); + assertThat(cont13.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13")); + SingleInstanceNode cont12 = ((SingleInstanceNode) cont13.children().get("cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + LeafNode l = ((LeafNode) cont12.children().get("leaf26")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.cont12.leaf26")); + + assertThat(cont13.children().containsKey("list9"), is(true)); + HolderNode list6Holder = ((ListHolderNode) cont13.children().get("list9")); + assertThat(list6Holder.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.list9")); + MultiInstanceNode list6 = ((MultiInstanceNode) list6Holder.child("0")); + assertThat(list6.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list6.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.list9[0].leaf27")); + + assertThat(cont13.children().containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) cont13.children().get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) cont13.children().get("ll9")); + assertThat(ll5Holder.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:cont1.cont2.cont4.test-augment:cont13.ll9[0]")); + } + } + } + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testRpcInput() throws SvcLogicException { + String uri = "test-yang:create-sfc"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_create-sfc.input.cont14.leaf28", "abc"); + params.put("test-yang_create-sfc.input.list10[0].leaf29", "abc"); + params.put("test-yang_create-sfc.input.leaf30", "abc"); + params.put("test-yang_create-sfc.input.ll10[0]", "abc"); + params.put("test-yang_create-sfc.input.cont15.leaf31", "abc"); + params.put("test-yang_create-sfc.input.cont13.cont12.leaf26", "abc"); + params.put("test-yang_create-sfc.input.cont13.list9[0].leaf27", "abc"); + params.put("test-yang_create-sfc.input.cont13.leaf28", "abc"); + params.put("test-yang_create-sfc.input.cont13.ll9[0]", "abc"); + params.put("test-yang_create-sfc.input.test-augment_leaf36", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + PropertiesNode input = childNodes.get("input"); + assertThat(input.uri(), is("test-yang:create-sfc.input")); + for (Map.Entry<Object, Collection<PropertiesNode>> augToChild + : node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> child = augToChild.getValue(); + if (!child.isEmpty()) { + List<String> expectedNodes = new LinkedList<>(); + expectedNodes.add("test-yang:create-sfc.input.test-augment:leaf36"); + assertThat(expectedNodes.size(), is(child.size())); + for (PropertiesNode pNode : child) { + assertThat(expectedNodes.contains(pNode.uri()), is(true)); + LeafNode leaf37 = ((LeafNode) pNode); + assertThat(leaf37.name(), is("leaf36")); + assertThat(leaf37.uri(), is("test-yang:create-sfc.input.test-augment:leaf36")); + } + } + } + childNodes = ((InnerNode) input).children(); + + assertThat(childNodes.containsKey("cont14"), is(true)); + SingleInstanceNode cont14 = ((SingleInstanceNode) childNodes.get("cont14")); + assertThat(cont14.uri(), is("test-yang:create-sfc.input.cont14")); + assertThat(cont14.children().containsKey("leaf28"), is(true)); + LeafNode l = ((LeafNode) cont14.children().get("leaf28")); + assertThat(l.uri(), is("test-yang:create-sfc.input.cont14.leaf28")); + + assertThat(childNodes.containsKey("list10"), is(true)); + HolderNode list10Holder = ((ListHolderNode) childNodes.get("list10")); + assertThat(list10Holder.uri(), is("test-yang:create-sfc.input.list10")); + MultiInstanceNode list10 = ((MultiInstanceNode) list10Holder.child("0")); + assertThat(list10.uri(), is("test-yang:create-sfc.input.list10[0]")); + Map<String, DataNodeChild> list10Child = list10.children(); + assertThat(list10Child.containsKey("leaf29"), is(true)); + l = ((LeafNode) list10Child.get("leaf29")); + assertThat(l.uri(), is("test-yang:create-sfc.input.list10[0].leaf29")); + + assertThat(childNodes.containsKey("leaf30"), is(true)); + LeafNode leaf30 = ((LeafNode) childNodes.get("leaf30")); + assertThat(leaf30.name(), is("leaf30")); + assertThat(leaf30.uri(), is("test-yang:create-sfc.input.leaf30")); + + LeafListHolderNode ll10Holder = ((LeafListHolderNode) childNodes.get("ll10")); + assertThat(ll10Holder.uri(), is("test-yang:create-sfc.input.ll10")); + assertThat(ll10Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll10Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:create-sfc.input.ll10[0]")); + + assertThat(childNodes.containsKey("cont15"), is(true)); + SingleInstanceNode cont15 = ((SingleInstanceNode) childNodes.get("cont15")); + assertThat(cont15.uri(), is("test-yang:create-sfc.input.cont15")); + assertThat(cont15.children().containsKey("leaf31"), is(true)); + l = ((LeafNode) cont15.children().get("leaf31")); + assertThat(l.uri(), is("test-yang:create-sfc.input.cont15.leaf31")); + + assertThat(childNodes.containsKey("cont13"), is(true)); + SingleInstanceNode cont13 = ((SingleInstanceNode) childNodes.get("cont13")); + assertThat(cont13.uri(), is("test-yang:create-sfc.input.cont13")); + SingleInstanceNode cont12 = ((SingleInstanceNode) cont13.children().get("cont12")); + assertThat(cont12.uri(), is("test-yang:create-sfc.input.cont13.cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + l = ((LeafNode) cont12.children().get("leaf26")); + assertThat(l.uri(), is("test-yang:create-sfc.input.cont13.cont12.leaf26")); + + assertThat(cont13.children().containsKey("list9"), is(true)); + HolderNode list9Holder = ((ListHolderNode) cont13.children().get("list9")); + assertThat(list9Holder.uri(), is("test-yang:create-sfc.input.cont13.list9")); + MultiInstanceNode list9 = ((MultiInstanceNode) list9Holder.child("0")); + assertThat(list9.uri(), is("test-yang:create-sfc.input.cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list9.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-yang:create-sfc.input.cont13.list9[0].leaf27")); + + assertThat(cont13.children().containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) cont13.children().get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-yang:create-sfc.input.cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) cont13.children().get("ll9")); + assertThat(ll5Holder.uri(), is("test-yang:create-sfc.input.cont13.ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:create-sfc.input.cont13.ll9[0]")); + + Map<String, String> output = ser.decode(node); + assertThat(output.size(), is(params.size())); + for (Map.Entry<String, String> entry : output.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testRpcOutput() throws SvcLogicException { + String uri = "test-yang:create-sfc"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_create-sfc.output.cont16.leaf32", "abc"); + params.put("test-yang_create-sfc.output.list11[0].leaf33", "abc"); + params.put("test-yang_create-sfc.output.leaf34", "abc"); + params.put("test-yang_create-sfc.output.ll11[0]", "abc"); + params.put("test-yang_create-sfc.output.cont17.leaf35", "abc"); + params.put("test-yang_create-sfc.output.cont13.cont12.leaf26", "abc"); + params.put("test-yang_create-sfc.output.cont13.list9[0].leaf27", "abc"); + params.put("test-yang_create-sfc.output.cont13.leaf28", "abc"); + params.put("test-yang_create-sfc.output.cont13.ll9[0]", "abc"); + params.put("test-yang_create-sfc.output.test-augment_leaf37", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + PropertiesNode output = childNodes.get("output"); + assertThat(output.uri(), is("test-yang:create-sfc.output")); + for (Map.Entry<Object, Collection<PropertiesNode>> augmentationToChild : + node.augmentations().asMap().entrySet()) { + Collection<PropertiesNode> c = augmentationToChild.getValue(); + if(!c.isEmpty()) { + List<String> expectedNodes = new LinkedList<>(); + expectedNodes.add("test-yang:create-sfc.output.test-augment:leaf37"); + assertThat(expectedNodes.size(), is(expectedNodes)); + for (PropertiesNode pNode : c) { + assertThat(expectedNodes.contains(pNode.uri()), is(true)); + LeafNode leaf37 = ((LeafNode) pNode); + assertThat(leaf37.name(), is("leaf37")); + assertThat(leaf37.uri(), is("test-yang:create-sfc.output.test-augment:leaf37")); + } + } + } + childNodes = ((InnerNode) output).children(); + + assertThat(childNodes.containsKey("cont16"), is(true)); + SingleInstanceNode cont16 = ((SingleInstanceNode) childNodes.get("cont16")); + assertThat(cont16.uri(), is("test-yang:create-sfc.output.cont16")); + assertThat(cont16.children().containsKey("leaf32"), is(true)); + LeafNode l = ((LeafNode) cont16.children().get("leaf32")); + assertThat(l.uri(), is("test-yang:create-sfc.output.cont16.leaf32")); + + assertThat(childNodes.containsKey("list11"), is(true)); + HolderNode list11Holder = ((ListHolderNode) childNodes.get("list11")); + assertThat(list11Holder.uri(), is("test-yang:create-sfc.output.list11")); + MultiInstanceNode list11 = ((MultiInstanceNode) list11Holder.child("0")); + assertThat(list11.uri(), is("test-yang:create-sfc.output.list11[0]")); + Map<String, DataNodeChild> list11Child = list11.children(); + assertThat(list11Child.containsKey("leaf33"), is(true)); + l = ((LeafNode) list11Child.get("leaf33")); + assertThat(l.uri(), is("test-yang:create-sfc.output.list11[0].leaf33")); + + assertThat(childNodes.containsKey("leaf34"), is(true)); + LeafNode leaf34 = ((LeafNode) childNodes.get("leaf34")); + assertThat(leaf34.name(), is("leaf34")); + assertThat(leaf34.uri(), is("test-yang:create-sfc.output.leaf34")); + + LeafListHolderNode ll10Holder = ((LeafListHolderNode) childNodes.get("ll11")); + assertThat(ll10Holder.uri(), is("test-yang:create-sfc.output.ll11")); + assertThat(ll10Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll10Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:create-sfc.output.ll11[0]")); + + assertThat(childNodes.containsKey("cont17"), is(true)); + SingleInstanceNode cont17 = ((SingleInstanceNode) childNodes.get("cont17")); + assertThat(cont17.uri(), is("test-yang:create-sfc.output.cont17")); + assertThat(cont17.children().containsKey("leaf35"), is(true)); + l = ((LeafNode) cont17.children().get("leaf35")); + assertThat(l.uri(), is("test-yang:create-sfc.output.cont17.leaf35")); + + assertThat(childNodes.containsKey("cont13"), is(true)); + SingleInstanceNode cont13 = ((SingleInstanceNode) childNodes.get("cont13")); + assertThat(cont13.uri(), is("test-yang:create-sfc.output.cont13")); + SingleInstanceNode cont12 = ((SingleInstanceNode) cont13.children().get("cont12")); + assertThat(cont12.uri(), is("test-yang:create-sfc.output.cont13.cont12")); + assertThat(cont12.children().containsKey("leaf26"), is(true)); + l = ((LeafNode) cont12.children().get("leaf26")); + assertThat(l.uri(), is("test-yang:create-sfc.output.cont13.cont12.leaf26")); + + assertThat(cont13.children().containsKey("list9"), is(true)); + HolderNode list9Holder = ((ListHolderNode) cont13.children().get("list9")); + assertThat(list9Holder.uri(), is("test-yang:create-sfc.output.cont13.list9")); + MultiInstanceNode list9 = ((MultiInstanceNode) list9Holder.child("0")); + assertThat(list9.uri(), is("test-yang:create-sfc.output.cont13.list9[0]")); + Map<String, DataNodeChild> list6Child = list9.children(); + assertThat(list6Child.containsKey("leaf27"), is(true)); + l = ((LeafNode) list6Child.get("leaf27")); + assertThat(l.uri(), is("test-yang:create-sfc.output.cont13.list9[0].leaf27")); + + assertThat(cont13.children().containsKey("leaf28"), is(true)); + LeafNode leaf12 = ((LeafNode) cont13.children().get("leaf28")); + assertThat(leaf12.name(), is("leaf28")); + assertThat(leaf12.uri(), is("test-yang:create-sfc.output.cont13.leaf28")); + + LeafListHolderNode ll5Holder = ((LeafListHolderNode) cont13.children().get("ll9")); + assertThat(ll5Holder.uri(), is("test-yang:create-sfc.output.cont13.ll9")); + assertThat(ll5Holder.children().containsKey("0"), is(true)); + l = ((LeafNode) ll5Holder.children().get("0")); + assertThat(l.uri(), is("test-yang:create-sfc.output.cont13.ll9[0]")); + + Map<String, String> output1 = ser.decode(node); + assertThat(output1.size(), is(params.size())); + for (Map.Entry<String, String> entry : output1.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testContainerSameName() throws SvcLogicException { + String uri = "test-yang:cont18"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont18.cont18.list12[0].list12[0].leaf36", "abc"); + params.put("test-yang_cont18.cont18.list12[0].leaf36", "hi"); + params.put("test-yang_cont18.cont18.list12[1].list12[0].leaf36", "xyz"); + params.put("test-yang_cont18.cont18.list12[1].list12[1].leaf36", "hey!"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + + assertThat(childNodes.containsKey("cont18"), is(true)); + node = childNodes.get("cont18"); + assertThat(node.uri(), is("test-yang:cont18.cont18")); + childNodes = ((InnerNode) node).children(); + + assertThat(childNodes.containsKey("list12"), is(true)); + HolderNode holder = ((ListHolderNode) childNodes.get("list12")); + assertThat(holder.uri(), is("test-yang:cont18.cont18.list12")); + MultiInstanceNode node1 = ((MultiInstanceNode) holder.child("0")); + assertThat(node1.uri(), is("test-yang:cont18.cont18.list12[0]")); + Map<String, DataNodeChild> list12Child = node1.children(); + + assertThat(list12Child.containsKey("leaf36"), is(true)); + LeafNode leaf = ((LeafNode) list12Child.get("leaf36")); + assertThat(leaf.value(), is("hi")); + assertThat(leaf.uri(), is("test-yang:cont18.cont18.list12[0].leaf36")); + + assertThat(list12Child.containsKey("list12"), is(true)); + HolderNode holder1 = ((ListHolderNode) list12Child.get("list12")); + assertThat(holder1.uri(), is("test-yang:cont18.cont18.list12[0].list12")); + node1 = ((MultiInstanceNode) holder1.child("0")); + assertThat(node1.uri(), is("test-yang:cont18.cont18.list12[0].list12[0]")); + list12Child = node1.children(); + assertThat(list12Child.containsKey("leaf36"), is(true)); + leaf = ((LeafNode) list12Child.get("leaf36")); + assertThat(leaf.value(), is("abc")); + assertThat(leaf.uri(), is("test-yang:cont18.cont18.list12[0].list12[0].leaf36")); + + node1 = ((MultiInstanceNode) holder.child("1")); + assertThat(node1.uri(), is("test-yang:cont18.cont18.list12[1]")); + list12Child = node1.children(); + assertThat(list12Child.containsKey("list12"), is(true)); + holder = ((ListHolderNode) list12Child.get("list12")); + assertThat(holder.uri(), is("test-yang:cont18.cont18.list12[1].list12")); + node1 = ((MultiInstanceNode) holder.child("0")); + assertThat(node1.uri(), is("test-yang:cont18.cont18.list12[1].list12[0]")); + assertThat(node1.children().containsKey("leaf36"), is(true)); + leaf = ((LeafNode) node1.children().get("leaf36")); + assertThat(leaf.value(), is("xyz")); + assertThat(leaf.uri(), is("test-yang:cont18.cont18.list12[1].list12[0].leaf36")); + + node1 = ((MultiInstanceNode) holder.child("1")); + assertThat(node1.uri(), is("test-yang:cont18.cont18.list12[1].list12[1]")); + assertThat(node1.children().containsKey("leaf36"), is(true)); + leaf = ((LeafNode) node1.children().get("leaf36")); + assertThat(leaf.value(), is("hey!")); + assertThat(leaf.uri(), is("test-yang:cont18.cont18.list12[1].list12[1].leaf36")); + + Map<String, String> output1 = ser.decode(node); + assertThat(output1.size(), is(params.size())); + for (Map.Entry<String, String> entry : output1.entrySet()) { + assertTrue(params.containsKey(entry.getKey())); + } + } + + @Test + public void testPropertiesWithoutSchema() throws SvcLogicException { + String uri = "test-yang:cont18"; + Map<String, String> params = new HashMap<>(); + params.put("test-yang_cont18.leaf40", "abc"); + params.put("leaf41", "hi"); + params.put("test-yang_cont18.leaf41", "abc"); + + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + assertThat(childNodes.containsKey("leaf40"), is(true)); + node = childNodes.get("leaf40"); + assertThat(node.uri(), is("test-yang:cont18.leaf40")); + } + + @Test + public void testIdentityRef() throws SvcLogicException { + String uri = "identity-test:test"; + Map<String, String> params = new HashMap<>(); + params.put("identity-test_test.con1.interface", "identity-types" + + ":physical"); + params.put("identity-test_test.con1.interfaces.int-list[0].iden", "identity-test:Giga"); + params.put("identity-test_test.con1.interfaces.int-list[0].available.ll[0]", "identity-types:Loopback"); + params.put("identity-test_test.con1.interfaces.int-list[0].available.leaf1", "identity-types-second:Ethernet"); + params.put("identity-test_test.con1.interfaces.int-list[0].available.leaf2", "identity-types-second:iden2"); + InstanceIdentifierContext<?> iCtx = ParserIdentifier + .toInstanceIdentifier(uri, context, null); + + PropertiesNodeSerializer ser = new MdsalPropertiesNodeSerializer( + iCtx.getSchemaNode(), context, uri); + PropertiesNode node = ser.encode(params); + Map<String, PropertiesNode> childNodes = ((RootNode) node).children(); + assertThat(childNodes.containsKey("con1"), is(true)); + node = childNodes.get("con1"); + assertThat(node.uri(), is("identity-test:test.con1")); + LeafNode l = ((LeafNode) ((SingleInstanceNode) node).children().get("interface")); + assertThat(l.uri(), is("identity-test:test.con1.interface")); + assertThat(l.valueNs().moduleName(), is("identity-types")); + assertThat(l.valueNs().moduleNs().toString(), is("identity:list:ns:test:json:ser")); + + // identity type inside union + node = ((SingleInstanceNode) ((SingleInstanceNode) node).children().get("interfaces")); + node = ((ListHolderNode) ((SingleInstanceNode) node).children().get("int-list")); + node = ((MultiInstanceNode) ((ListHolderNode) node).children().get("0")); + l = ((LeafNode) ((MultiInstanceNode) node).children().get("iden")); + assertThat(l.uri(), is("identity-test:test.con1.interfaces.int-list[0].iden")); + assertThat(l.valueNs().moduleName(), is("identity-test")); + assertThat(l.valueNs().moduleNs().toString(), is("identity:ns:test:json:ser")); + + // leaf-list test + node = (SingleInstanceNode) ((MultiInstanceNode) node).children().get("available"); + LeafListHolderNode holder = (LeafListHolderNode) ((SingleInstanceNode) node).children().get("ll"); + l = ((LeafNode) holder.children().get("0")); + assertThat(l.uri(), is("identity-test:test.con1.interfaces.int-list[0].available.ll[0]")); + assertThat(l.valueNs().moduleName(), is("identity-types")); + assertThat(l.valueNs().moduleNs().toString(), is("identity:list:ns:test:json:ser")); + + // leaf-ref test + l = ((LeafNode) ((SingleInstanceNode) node).children().get("leaf1")); + assertThat(l.uri(), is("identity-test:test.con1.interfaces.int-list[0].available.leaf1")); + assertThat(l.valueNs().moduleName(), is("identity-types-second")); + assertThat(l.valueNs().moduleNs().toString(), is("identity:list:second:ns:test:json:ser")); + + // list of base identity test + l = ((LeafNode) ((SingleInstanceNode) node).children().get("leaf2")); + assertThat(l.uri(), is("identity-test:test.con1.interfaces.int-list[0].available.leaf2")); + assertThat(l.valueNs().moduleName(), is("identity-types-second")); + assertThat(l.valueNs().moduleNs().toString(), is("identity:list:second:ns:test:json:ser")); + } + + public static SchemaContext compileYangFile() throws FileNotFoundException { + String path = PropertiesSerializerTest.class.getResource("/yang").getPath(); + File dir = new File(path); + String[] fileList = dir.list(); + List<File> yangFiles = new ArrayList<File>(); + if (fileList == null) { + throw new FileNotFoundException("/yang"); + } + for (int i = 0; i < fileList.length; i++) { + final String fileName = fileList[i]; + if (new File(dir, fileName).isDirectory() == false) { + yangFiles.add(new File(dir, fileName)); + } + } + return YangParserTestUtils.parseYangFiles(yangFiles); + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/test/resources/yang/execution-service.yang b/plugins/restconf-client/provider/src/test/resources/yang/execution-service.yang new file mode 100644 index 000000000..d7cf68f12 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/execution-service.yang @@ -0,0 +1,43 @@ +module execution-service { + yang-version 1.1; + namespace "cds:workflow:rest"; + prefix "cds"; + + revision "2019-05-21"; + + container process { + container commonHeader { + leaf originatorId { + type string; + } + leaf requestId { + type string; + } + leaf subRequestId { + type string; + } + } + container actionIdentifiers { + leaf blueprintName { + type string; + } + leaf blueprintVersion { + type string; + } + leaf actionName { + type string; + } + leaf mode { + type string; + } + } + container payload { + leaf-list template-prefix { + type string; + } + container resource-assignment-request { + anyxml resource-assignment-properties; + } + } + } +} diff --git a/plugins/restconf-client/provider/src/test/resources/yang/identity-test.yang b/plugins/restconf-client/provider/src/test/resources/yang/identity-test.yang new file mode 100644 index 000000000..12ef717f6 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/identity-test.yang @@ -0,0 +1,77 @@ +module identity-test { + yang-version 1.1; + namespace "identity:ns:test:json:ser"; + prefix "id"; + + import identity-types { + prefix "type"; + } + + import identity-types-second { + prefix "sec"; + } + + revision "2013-07-15"; + + identity optical { + base type:int-type; + } + + identity Giga { + base type:physical; + } + + typedef available { + type identityref { + base "type:physical"; + } + } + + typedef typed{ + type union { + type int32; + type int8; + type identityref { + base type:int-type; + } + } + } + + container test { + leaf l { + type string; + } + container con1 { + leaf interface { + type identityref { + base "type:int-type"; + } + } + container interfaces { + list int-list { + key "iden"; + leaf iden { + type "id:typed"; + } + container available { + leaf-list ll { + type available; + } + leaf leaf1 { + type leafref { + path "../../iden"; + } + } + + leaf leaf2 { + type identityref { + base type:int-type; + base sec:iden1; + } + } + } + } + } + } + } +} diff --git a/plugins/restconf-client/provider/src/test/resources/yang/identity-types-second.yang b/plugins/restconf-client/provider/src/test/resources/yang/identity-types-second.yang new file mode 100644 index 000000000..98d6a6e60 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/identity-types-second.yang @@ -0,0 +1,25 @@ +module identity-types-second { + yang-version 1; + namespace "identity:list:second:ns:test:json:ser"; + prefix "sec"; + + import identity-types { + prefix "type"; + } + + revision "2013-07-15"; + + identity virtual { + base type:int-type; + } + + identity Ethernet { + base type:physical; + } + + identity iden1; + + identity iden2 { + base iden1; + } +} diff --git a/plugins/restconf-client/provider/src/test/resources/yang/identity-types.yang b/plugins/restconf-client/provider/src/test/resources/yang/identity-types.yang new file mode 100644 index 000000000..25c8fa54f --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/identity-types.yang @@ -0,0 +1,17 @@ +module identity-types { + yang-version 1; + namespace "identity:list:ns:test:json:ser"; + prefix "type"; + revision "2013-07-15"; + + identity int-type { + } + + identity physical { + base int-type; + } + + identity Loopback { + base physical; + } +} diff --git a/plugins/restconf-client/provider/src/test/resources/yang/test-augment.yang b/plugins/restconf-client/provider/src/test/resources/yang/test-augment.yang new file mode 100644 index 000000000..795000d39 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/test-augment.yang @@ -0,0 +1,106 @@ +module test-augment { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:augment"; + prefix "hello"; + + import test-yang { + prefix t; + } + + revision "2015-01-05" { + description "Initial revision of hello model"; + } + + augment "/t:cont1/t:cont2/t:cont4" { + container cont5 { + leaf leaf13 { + type empty; + } + } + list list7 { + leaf leaf14 { + type instance-identifier; + } + } + leaf leaf15 { + type string; + } + leaf-list ll6 { + type union { + type int32; + type enumeration { + enum "unbounded"; + } + } + } + uses "t:g1"; + } + + uses "t:g1"; + augment "/t:ch1/t:c1/t:cont8/t:cont6" { + choice ch2 { + case c3 { + leaf leaf21 { + type string; + } + } + case c4 { + leaf leaf22 { + type enumeration { + enum zero; + enum one; + enum seven { + value 7; + } + } + } + } + } + } + + augment "/t:ch1" { + case c5 { + container cont10 { + leaf leaf23 { + type string; + } + } + } + } + + augment "/t:ch1/t:c1" { + container cont7 { + leaf leaf24 { + type string; + } + } + } + + augment "/t:cont13/t:cont12" { + leaf leaf29 { + type string; + } + } + + augment "/t:create-sfc/t:input" { + leaf leaf36 { + type bits { + bit angle { + position 0; + } + bit degree { + position 1; + } + bit movement { + position 2; + } + } + } + } + + augment "/t:create-sfc/t:output" { + leaf leaf37 { + type boolean; + } + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/test/resources/yang/test-yang.yang b/plugins/restconf-client/provider/src/test/resources/yang/test-yang.yang new file mode 100644 index 000000000..b2bf06003 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/test-yang.yang @@ -0,0 +1,231 @@ +module test-yang { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:test"; + prefix "hello"; + + revision "2015-01-05" { + description "Initial revision of hello model"; + } + + container cont1 { + container cont2 { + container cont3 { + leaf leaf10 { + type string; + } + } + list list1 { + key "leaf1 leaf2"; + leaf leaf1 { + type empty; + } + leaf leaf2 { + type string; + } + leaf leaf3 { + type string; + } + leaf-list ll1 { + type string; + } + leaf-list ll2 { + type string; + } + container cont4 { + leaf leaf11 { + type string; + } + } + list list4 { + leaf leaf8 { + type string; + } + } + list list5 { + leaf leaf9 { + type string; + } + } + } + list list2 { + leaf leaf4 { + type string; + } + } + leaf leaf5 { + type string; + } + leaf leaf6 { + type string; + } + leaf-list ll3 { + type string; + } + leaf-list ll4 { + type string; + } + } + } + + augment "/cont1/cont2" { + container cont4 { + leaf leaf10 { + type string; + } + } + list list6 { + leaf leaf11 { + type string; + } + } + leaf leaf12 { + type string; + } + leaf-list ll5 { + type string; + } + } + + choice ch1 { + case c1 { + container cont8 { + container cont6 { + leaf leaf16 { + type string; + } + } + list list8 { + leaf leaf18 { + type string; + } + } + leaf leaf19 { + type string; + } + leaf-list ll7 { + type string; + } + } + } + case c2 { + container cont9 { + leaf leaf20 { + type string; + } + leaf-list ll8 { + type string; + } + container cont11 { + choice ch3 { + case c1 { + leaf leaf25 { + type string; + } + uses g1; + } + } + } + } + } + } + + grouping g1 { + container cont13 { + container cont12 { + leaf leaf26 { + type string; + } + } + list list9 { + leaf leaf27 { + type string; + } + } + leaf leaf28 { + type string; + } + leaf-list ll9 { + type string; + } + } + } + + uses g1; + + rpc create-sfc { + input { + container cont14 { + leaf leaf28 { + type string; + } + } + list list10 { + leaf leaf29 { + type string; + } + } + leaf leaf30 { + type string; + } + leaf-list ll10 { + type string; + } + choice ch3 { + case c1 { + container cont15 { + leaf leaf31 { + type string; + } + } + } + } + uses g1; + } + output { + container cont16 { + leaf leaf32 { + type string; + } + } + list list11 { + leaf leaf33 { + type string; + } + } + leaf leaf34 { + type string; + } + leaf-list ll11 { + type string; + } + choice ch4 { + case c1 { + container cont17 { + leaf leaf35 { + type string; + } + } + } + } + uses g1; + } + } + + container cont18 { + container cont18 { + list list12 { + list list12 { + leaf leaf36 { + type string; + } + } + leaf leaf36 { + type string; + } + } + } + leaf leaf40 { + type string; + } + } +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/test/resources/yang/test_augment_1_for_module.yang b/plugins/restconf-client/provider/src/test/resources/yang/test_augment_1_for_module.yang new file mode 100644 index 000000000..d2eeea7d1 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/test_augment_1_for_module.yang @@ -0,0 +1,108 @@ +module test_augment_1_for_module { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:test:augment:name"; + prefix "augment-name"; + + import test_name_of_the_module { + prefix aug; + } + + revision "2015-01-05" { + description "Initial revision of hello model"; + } + + augment "/aug:name_of_the_cont1/aug:name_of_the_cont2/aug:name_of_the_cont4" { + container name_of_the_cont5 { + leaf name_of_the_leaf13 { + type empty; + } + } + list name_of_the_list7 { + leaf name_of_the_leaf14 { + type instance-identifier; + } + } + leaf name_of_the_leaf15 { + type string; + } + leaf-list name_of_the_ll6 { + type union { + type int32; + type enumeration { + enum "unbounded"; + } + } + } + uses "aug:name_of_the_g1"; + } + + uses "aug:name_of_the_g1"; + + augment "/aug:name_of_the_cont1/aug:name_of_the_cont2/aug:name_of_the_cont3" { + choice name_of_the_ch2 { + case name_of_the_c3 { + leaf name_of_the_leaf21 { + type string; + } + } + case name_of_the_c4 { + leaf name_of_the_leaf22 { + type enumeration { + enum zero; + enum one; + enum seven { + value 7; + } + } + } + } + } + } + + augment "/aug:name_of_the_ch1" { + case name_of_the_c5 { + container name_of_the_cont10 { + leaf name_of_the_leaf23 { + type string; + } + } + } + } + + augment "/aug:name_of_the_ch1/aug:name_of_the_c1" { + container name_of_the_cont7 { + leaf name_of_the_leaf24 { + type string; + } + } + } + + augment "/aug:name_of_the_cont13/aug:name_of_the_cont12" { + leaf name_of_the_leaf29 { + type string; + } + } + + augment "/aug:name_of_the_create-sfc/aug:input" { + leaf name_of_the_leaf36 { + type bits { + bit angle { + position 0; + } + bit degree { + position 1; + } + bit movement { + position 2; + } + } + } + } + + augment "/aug:name_of_the_create-sfc/aug:output" { + leaf leaf37 { + type boolean; + } + } + +}
\ No newline at end of file diff --git a/plugins/restconf-client/provider/src/test/resources/yang/test_name_of_the_module.yang b/plugins/restconf-client/provider/src/test/resources/yang/test_name_of_the_module.yang new file mode 100644 index 000000000..973475ee7 --- /dev/null +++ b/plugins/restconf-client/provider/src/test/resources/yang/test_name_of_the_module.yang @@ -0,0 +1,231 @@ +module test_name_of_the_module { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:test:name"; + prefix "name"; + + revision "2015-01-05" { + description "Initial revision of hello model"; + } + + container name_of_the_cont1 { + container name_of_the_cont2 { + container name_of_the_cont3 { + leaf name_of_the_leaf10 { + type string; + } + } + list name_of_the_list1 { + key "name_of_the_leaf1 name_of_the_leaf2"; + leaf name_of_the_leaf1 { + type empty; + } + leaf name_of_the_leaf2 { + type string; + } + leaf name_of_the_leaf3 { + type string; + } + leaf-list name_of_the_ll1 { + type string; + } + leaf-list name_of_the_ll2 { + type string; + } + container name_of_the_cont4 { + leaf name_of_the_leaf11 { + type string; + } + } + list name_of_the_list4 { + leaf name_of_the_leaf8 { + type string; + } + } + list name_of_the_list5 { + leaf name_of_the_leaf9 { + type string; + } + } + } + list name_of_the_list2 { + leaf name_of_the_leaf4 { + type string; + } + } + leaf name_of_the_leaf5 { + type string; + } + leaf name_of_the_leaf6 { + type string; + } + leaf-list name_of_the_ll3 { + type string; + } + leaf-list name_of_the_ll4 { + type string; + } + } + } + + augment "/name_of_the_cont1/name_of_the_cont2" { + container name_of_the_cont4 { + leaf name_of_the_leaf10 { + type string; + } + } + list name_of_the_list6 { + leaf name_of_the_leaf11 { + type string; + } + } + leaf name_of_the_leaf12 { + type string; + } + leaf-list name_of_the_ll5 { + type string; + } + } + + choice name_of_the_ch1 { + case name_of_the_c1 { + container name_of_the_cont8 { + container name_of_the_cont6 { + leaf name_of_the_leaf16 { + type string; + } + } + list name_of_the_list8 { + leaf name_of_the_leaf18 { + type string; + } + } + leaf name_of_the_leaf19 { + type string; + } + leaf-list name_of_the_ll7 { + type string; + } + } + } + case name_of_the_c2 { + container name_of_the_cont9 { + leaf name_of_the_leaf20 { + type string; + } + leaf-list name_of_the_ll8 { + type string; + } + container name_of_the_cont11 { + choice name_of_the_ch3 { + case name_of_the_c1 { + leaf name_of_the_leaf25 { + type string; + } + uses name_of_the_g1; + } + } + } + } + } + } + + grouping name_of_the_g1 { + container name_of_the_cont13 { + container name_of_the_cont12 { + leaf name_of_the_leaf26 { + type string; + } + } + list name_of_the_list9 { + leaf name_of_the_leaf27 { + type string; + } + } + leaf name_of_the_leaf28 { + type string; + } + leaf-list name_of_the_ll9 { + type string; + } + } + } + + uses name_of_the_g1; + + rpc name_of_the_create-sfc { + input { + container name_of_the_cont14 { + leaf name_of_the_leaf28 { + type string; + } + } + list name_of_the_list10 { + leaf name_of_the_leaf29 { + type string; + } + } + leaf name_of_the_leaf30 { + type string; + } + leaf-list name_of_the_ll10 { + type string; + } + choice name_of_the_ch3 { + case name_of_the_c1 { + container name_of_the_cont15 { + leaf name_of_the_leaf31 { + type string; + } + } + } + } + uses name_of_the_g1; + } + output { + container name_of_the_cont16 { + leaf name_of_the_leaf32 { + type string; + } + } + list name_of_the_list11 { + leaf name_of_the_leaf33 { + type string; + } + } + leaf name_of_the_leaf34 { + type string; + } + leaf-list name_of_the_ll11 { + type string; + } + choice name_of_the_ch4 { + case name_of_the_c1 { + container name_of_the_cont17 { + leaf name_of_the_leaf35 { + type string; + } + } + } + } + uses name_of_the_g1; + } + } + + container name_of_the_cont18 { + container name_of_the_cont18 { + list name_of_the_list12 { + list name_of_the_list12 { + leaf name_of_the_leaf36 { + type string; + } + } + leaf name_of_the_leaf36 { + type string; + } + } + } + leaf name_of_the_leaf40 { + type string; + } + } +}
\ No newline at end of file diff --git a/plugins/sshapi-call-node/.gitignore b/plugins/sshapi-call-node/.gitignore new file mode 100755 index 000000000..3c30c3505 --- /dev/null +++ b/plugins/sshapi-call-node/.gitignore @@ -0,0 +1,35 @@ +#####standard .git ignore entries##### + +## IDE Specific Files ## +org.eclipse.core.resources.prefs +.classpath +.project +.settings +.idea +.externalToolBuilders +maven-eclipse.xml +workspace + +## Compilation Files ## +*.class +**/target +target +target-ide +MANIFEST.MF + +## Misc Ignores (OS specific etc) ## +bin/ +dist +*~ +*.ipr +*.iml +*.iws +classes +out/ +.DS_STORE +.metadata +*.log + +## Folders which contain auto generated source code ## +yang-gen-config +yang-gen-sal diff --git a/plugins/sshapi-call-node/installer/pom.xml b/plugins/sshapi-call-node/installer/pom.xml new file mode 100755 index 000000000..de7509dbc --- /dev/null +++ b/plugins/sshapi-call-node/installer/pom.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sshapi-call-node-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: sshapi-call-node :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-sshapi-call-node</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sshapi-call-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/sshapi-call-node/installer/src/assembly/assemble_installer_zip.xml b/plugins/sshapi-call-node/installer/src/assembly/assemble_installer_zip.xml new file mode 100644 index 000000000..b2e792123 --- /dev/null +++ b/plugins/sshapi-call-node/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,59 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/sshapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/sshapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..2a6344697 --- /dev/null +++ b/plugins/sshapi-call-node/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,49 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/sshapi-call-node/installer/src/main/resources/scripts/install-feature.sh b/plugins/sshapi-call-node/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..8e516a247 --- /dev/null +++ b/plugins/sshapi-call-node/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2018 Samsung Electronics. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -nd ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/sshapi-call-node/pom.xml b/plugins/sshapi-call-node/pom.xml new file mode 100755 index 000000000..a9dc52e4b --- /dev/null +++ b/plugins/sshapi-call-node/pom.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sshapi-call-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: sshapi-call-node</name> + <description>This is an implementation of DG Execute Node that makes a call to an external REST API</description> + + <modules> + <module>provider</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/sshapi-call-node/provider/ReadMe.md b/plugins/sshapi-call-node/provider/ReadMe.md new file mode 100644 index 000000000..beaadcda8 --- /dev/null +++ b/plugins/sshapi-call-node/provider/ReadMe.md @@ -0,0 +1,32 @@ +SSHApi-Call-Node Plugin: + +Parameters List that managed in from Directed Graphs: + +Input: +Url -> Mandatory -> url to make the SSH connection request to. +Port -> Mandatory -> port to make the SSH connection request to. +AuthType -> Optional -> Type of authentiation to be used BASIC or sshKey based -> true or false +User -> Optional -> user name to use for ssh basic authentication -> sdnc_ws +Password -> Optional -> unencrypted password to use for ssh basic authentication -> plain_password +SshKey -> Optional -> Consumer SSH key to use for ssh authentication -> plain_key +ExecTimeout -> Optional -> SSH command execution timeout -> plain_key +Retry -> Optional -> Make ssh connection with default retry policy -> plain_key +Cmd -> Mandatory -> ssh command to be executed on the server. -> get post put delete patch +EnvParameters -> Optional -> A JSON dictionary which should list key value pairs to be passed to the command execution. These values would correspond to instance specific parameters that a command may need to execute an action. +FileParameters -> Optional -> A JSON dictionary where keys are filenames and values are contents of files. The SSH Server will utilize this feature to generate files with keys as filenames and values as content. This attribute can be used to generate files that a command may require as part of execution. +ConvertResponse -> Optional -> whether the response should be converted to properties -> true or false +ResponseType -> Optional -> If we know the response is to be in a specific format (supported are JSON, XML and NONE) +ResponsePrefix -> Optional -> location the response will be written to in context memory +listName[i] -> Optional -> Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details + +Output: +"sshApi.call.node.status" -> SSH Exit status code is set in here. +"sshApi.call.node.stdout" -> SSH command execution result is put in here. +"sshApi.call.node.stderr" -> SSH execution failure message is put in here. + + + +API methods that are exposed: +1) execCommand +2) execWithStatusCheck: Throws exception if the exit status is not successful. +3) execCommandWithPty
\ No newline at end of file diff --git a/plugins/sshapi-call-node/provider/pom.xml b/plugins/sshapi-call-node/provider/pom.xml new file mode 100755 index 000000000..49bceb122 --- /dev/null +++ b/plugins/sshapi-call-node/provider/pom.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sshapi-call-node-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: sshapi-call-node :: ${project.artifactId}</name> + + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-core-artifacts</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <scope>provided</scope> + </dependency> + <!-- ILLEGAL DEPENDENCY : creates circular dependency between CCSDK and APPC --> + <dependency> + <groupId>org.onap.appc</groupId> + <artifactId>appc-ssh-adapter-api</artifactId> + <version>1.3.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.jettison</groupId> + <artifactId>jettison</artifactId> + </dependency> + <dependency> + <groupId>com.att.eelf</groupId> + <artifactId>eelf-core</artifactId> + </dependency> + </dependencies> +</project> diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java new file mode 100644 index 000000000..146188fb9 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/SshApiCallNode.java @@ -0,0 +1,283 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP : APPC + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ============================================================================= + * Copyright (C) 2018 Samsung Electronics. All rights + reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall; + +import java.io.ByteArrayOutputStream; +import java.util.Map; + +import org.onap.appc.adapter.ssh.SshAdapter; +import org.onap.appc.adapter.ssh.SshConnection; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.onap.ccsdk.sli.plugins.sshapicall.model.AuthType; +import org.onap.ccsdk.sli.plugins.sshapicall.model.Parameters; +import org.onap.ccsdk.sli.plugins.sshapicall.model.ParseParam; + + +public class SshApiCallNode implements SvcLogicJavaPlugin { + + private static final EELFLogger logger = EELFManager.getInstance().getApplicationLogger(); + + /** + * Output parameter - SSH command execution status. + */ + private String PARAM_OUT_status = "sshApi.call.node.status"; + + /** + * Output parameter - content of SSH command stdout. + */ + private String PARAM_OUT_stdout = "sshApi.call.node.stdout"; + + /** + * Output parameter - content of SSH command stderr. + */ + private String PARAM_OUT_stderr = "sshApi.call.node.stderr"; + + /** + * Testing parameter - content of mocked SSH. + */ + private boolean PARAM_TEST_MODE = false; + private String PARAM_OUT_MESSAGE = "TestOut"; + + + /** + * Default success status. + */ + private int DEF_SUCCESS_STATUS = 0; + + + + private SshAdapter sshAdapter; + + /** + * Used for jUnit test and testing interface + */ + public SshApiCallNode(boolean mode) { + PARAM_TEST_MODE = mode; + } + + public SshApiCallNode() { + PARAM_TEST_MODE = false; + } + + public void setSshAdapter(SshAdapter sshAdapter) { + this.sshAdapter = sshAdapter; + } + + /** + * Allows Directed Graphs the ability to interact with SSH APIs. + * @param params HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td></tr> + * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> + * </tbody> + * </table> + * Exec remote command over SSH. Return command execution status. + * Command output is written to out or err stream. + * + * @param ctx Reference to context memory + */ + + public void execCommand(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + execSshCommand(params, ctx, false); + } + + private void execSshCommand(Map<String, String> params, SvcLogicContext ctx, boolean withPty) throws SvcLogicException { + ParseParam parser = new ParseParam(); + Parameters p = parser.getParameters(params); + logger.debug("=> Connecting to SSH server..."); + SshConnection sshConnection = null; + int status = -1; + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + String stdoutRes = ""; + String stderrRes = ""; + try { + if (!PARAM_TEST_MODE) { + sshConnection = getSshConnection(p); + sshConnection.connect(); + logger.debug("=> Connected to SSH server..."); + logger.debug("=> Running SSH command..."); + sshConnection.setExecTimeout(p.sshExecTimeout); + if (withPty) { + status = sshConnection.execCommandWithPty(parser.makeCommand(params), stdout); + stderr = stdout; + } + else + status = sshConnection.execCommand(parser.makeCommand(params), stdout, stderr); + stdoutRes = stdout.toString(); + stderrRes = stderr.toString(); + + } else { + if (("true").equalsIgnoreCase(params.get("TestFail"))) + status = 202; + else + status = DEF_SUCCESS_STATUS; + stdoutRes = params.get(PARAM_OUT_MESSAGE); + } + logger.debug("=> executed SSH command"); + + if(status == DEF_SUCCESS_STATUS) { + parser.parseOutput(ctx, stdoutRes); + } + ctx.setAttribute(PARAM_OUT_status, String.format("%01d", status)); + ctx.setAttribute(PARAM_OUT_stdout, stdoutRes); + ctx.setAttribute(PARAM_OUT_stderr, stderrRes); + } catch (Exception e){ + throw new SvcLogicException("Exception in SSH adaptor : " + e.getMessage()); + } finally { + if (sshConnection != null) + sshConnection.disconnect(); + } + } + + private SshConnection getSshConnection(Parameters p) throws SvcLogicException { + if (p.authtype == AuthType.BASIC) + return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshapiUser, p.sshapiPassword); + // This is not supported yet in the API, patch has already been added to APPC + else if (p.authtype == AuthType.KEY){ + throw new SvcLogicException("SSH Key based Auth method not supported"); + } + else if (p.authtype == AuthType.NONE){ + throw new SvcLogicException("SSH Auth type required, BASIC auth in support"); + } + else if (p.authtype == AuthType.UNSPECIFIED){ + if (p.sshapiUser != null && p.sshapiPassword != null) + return sshAdapter.getConnection(p.sshapiUrl, p.sshapiPort, p.sshapiUser, p.sshapiPassword); + else if (p.sshKey != null) + throw new SvcLogicException("SSH Key based Auth method not supported"); + } + throw new SvcLogicException("SSH Auth type required, BASIC auth in support"); + } + + + /** + * Allows Directed Graphs the ability to interact with SSH APIs. + * @param params HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td></tr> + * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> + * </tbody> + * </table> + * Exec remote command over SSH. Return command execution status. + * Command output is written to out or err stream. + * + * @param ctx Reference to context memory + */ + + public void execWithStatusCheck(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + execCommand(params, ctx); + parseResponse(ctx); + } + + private void parseResponse (SvcLogicContext ctx) throws SvcLogicException { + int status = Integer.parseInt(ctx.getAttribute(PARAM_OUT_status)); + if(status != DEF_SUCCESS_STATUS) { + StringBuilder errmsg = new StringBuilder(); + errmsg.append("SSH command returned error status [").append(status).append(']'); + String stderr = ctx.getAttribute(PARAM_OUT_stderr); + if((stderr != null) && !stderr.isEmpty()) { + errmsg.append(". Error: [").append(stderr).append(']'); + } + throw new SvcLogicException(errmsg.toString()); + } + } + + /** + * Allows Directed Graphs the ability to interact with SSH APIs. + * @param params HashMap<String,String> of parameters passed by the DG to this function + * <table border="1"> + * <thead><th>parameter</th><th>Mandatory/Optional</th><th>description</th><th>example values</th></thead> + * <tbody> + * <tr><td>Url</td><td>Mandatory</td><td>url to make the SSH connection request to.</td></tr> + * <tr><td>Port</td><td>Mandatory</td><td>port to make the SSH connection request to.</td></tr> + * <tr><td>User</td><td>Optional</td><td>user name to use for ssh basic authentication</td><td>sdnc_ws</td></tr> + * <tr><td>Password</td><td>Optional</td><td>unencrypted password to use for ssh basic authentication</td><td>plain_password</td></tr> + * <tr><td>SshKey</td><td>Optional</td><td>Consumer SSH key to use for ssh authentication</td><td>plain_key</td></tr> + * <tr><td>ExecTimeout</td><td>Optional</td><td>SSH command execution timeout</td><td>plain_key</td></tr> + * <tr><td>Retry</td><td>Optional</td><td>Make ssh connection with default retry policy</td><td>plain_key</td></tr> + * <tr><td>Cmd</td><td>Mandatory</td><td>ssh command to be executed on the server.</td><td>get post put delete patch</td></tr> + * <tr><td>ResponsePrefix</td><td>Optional</td><td>location the response will be written to in context memory</td></tr> + * <tr><td>ResponseType</td><td>Optional</td><td>If we know the response is to be in a specific format (supported are JSON, XML and NONE) </td></tr> + * <tr><td>listName[i]</td><td>Optional</td><td>Used for processing XML responses with repeating elements.</td>vpn-information.vrf-details<td></td></tr> + * <tr><td>ConvertResponse </td><td>Optional</td><td>whether the response should be converted</td><td>true or false</td></tr> + * <tr><td>AuthType</td><td>Optional</td><td>Type of authentiation to be used BASIC or sshKey based</td><td>true or false</td></tr> + * <tr><td>EnvParameters</td><td>Optional</td><td>A JSON dictionary which should list key value pairs to be passed to the command execution. + * These values would correspond to instance specific parameters that a command may need to execute an action.</td></tr> + * <tr><td>FileParameters</td><td>Optional</td><td>A JSON dictionary where keys are filenames and values are contents of files. + * The SSH Server will utilize this feature to generate files with keys as filenames and values as content. + * This attribute can be used to generate files that a command may require as part of execution.</td></tr> + * </tbody> + * </table> + * Exec remote command over SSH. Return command execution status. + * Command output is written to out or err stream. + * + * @param ctx Reference to context memory + */ + public void execCommandWithPty(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + execSshCommand(params, ctx, true); + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/AuthType.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/AuthType.java new file mode 100644 index 000000000..806c80890 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/AuthType.java @@ -0,0 +1,41 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * + * 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.ccsdk.sli.plugins.sshapicall.model; + +public enum AuthType { + NONE, BASIC, KEY, UNSPECIFIED; + + public static AuthType fromString(String s) { + if ("basic".equalsIgnoreCase(s)) + return BASIC; + if ("key".equalsIgnoreCase(s)) + return KEY; + if ("unspecified".equalsIgnoreCase(s)) + return UNSPECIFIED; + if ("none".equalsIgnoreCase(s)) + return NONE; + throw new IllegalArgumentException("Invalid value for format: " + s); + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Format.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Format.java new file mode 100644 index 000000000..43c3c38d6 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Format.java @@ -0,0 +1,39 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * + * 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.ccsdk.sli.plugins.sshapicall.model; + +public enum Format { + JSON, XML, NONE; + + public static Format fromString(String s) { + if ("json".equalsIgnoreCase(s)) + return JSON; + if ("xml".equalsIgnoreCase(s)) + return XML; + if ("none".equalsIgnoreCase(s)) + return NONE; + throw new IllegalArgumentException("Invalid value for format: " + s); + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java new file mode 100644 index 000000000..3d48a790f --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/JsonParser.java @@ -0,0 +1,93 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall.model; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class JsonParser { + + private static final Logger log = LoggerFactory.getLogger(JsonParser.class); + + private JsonParser() { + // Preventing instantiation of the same. + } + + @SuppressWarnings("unchecked") + public static Map<String, String> convertToProperties(String s) + throws JSONException { + + checkNotNull(s, "Input should not be null."); + + JSONObject json = new JSONObject(s); + Map<String, Object> wm = new HashMap<>(); + Iterator<String> ii = json.keys(); + while (ii.hasNext()) { + String key1 = ii.next(); + wm.put(key1, json.get(key1)); + } + + Map<String, String> mm = new HashMap<>(); + + while (!wm.isEmpty()) + for (String key : new ArrayList<>(wm.keySet())) { + Object o = wm.get(key); + wm.remove(key); + + if (o instanceof Boolean || o instanceof Number || o instanceof String) { + mm.put(key, o.toString()); + + log.info("Added property: {} : {}", key, o.toString()); + } else if (o instanceof JSONObject) { + JSONObject jo = (JSONObject) o; + Iterator<String> i = jo.keys(); + while (i.hasNext()) { + String key1 = i.next(); + wm.put(key + "." + key1, jo.get(key1)); + } + } else if (o instanceof JSONArray) { + JSONArray ja = (JSONArray) o; + mm.put(key + "_length", String.valueOf(ja.length())); + + log.info("Added property: {}_length: {}", key, String.valueOf(ja.length())); + + for (int i = 0; i < ja.length(); i++) + wm.put(key + '[' + i + ']', ja.get(i)); + } + } + return mm; + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Parameters.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Parameters.java new file mode 100644 index 000000000..fa2371e88 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/Parameters.java @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall.model; + +import java.util.Set; + +public class Parameters { + public String sshapiUrl; + public int sshapiPort; + public String sshapiUser; + public String sshapiPassword; + public String sshKey; + public long sshExecTimeout; + public String sshFileParameters; + public String sshEnvParameters; + public boolean sshWithRetry; + public String cmd; + public String responsePrefix; + public Format responseType; + + public Set<String> listNameList; + public boolean convertResponse; + public AuthType authtype; +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java new file mode 100644 index 000000000..35c1087d8 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/ParseParam.java @@ -0,0 +1,275 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * Modifications Copyright © 2018 IBM + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall.model; + +import com.google.common.base.Strings; +import org.json.JSONException; +import org.json.JSONObject; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +public class ParseParam { + private static final Logger log = LoggerFactory.getLogger(ParseParam.class); + /** + * Default SSH command timeout + */ + private long dEF_timeout = 12000; + /** + * Default SSH connection port. + */ + private int dEF_port = 22; + /** + * Default SSH command timeout + */ + private final String FILE_PARAMETERS_OPT_KEY = "FileParameters"; + public final String SSH_ResponsePrefix = "ResponsePrefix"; + /** + * Default SSH connection port. + */ + private final String ENV_PARAMETERS_OPT_KEY = "EnvParameters"; + private Parameters p = new Parameters(); + + public Parameters getParameters(Map<String, String> paramMap) throws SvcLogicException { + p.sshapiUrl = parseParam(paramMap, "Url", true, null); + p.sshapiPort = Integer.parseInt(parseParam(paramMap, "Port", true, Integer.toString(dEF_port))); + p.sshapiUser = parseParam(paramMap, "User", false, null); + p.sshapiPassword = parseParam(paramMap, "Password", false, null); + p.sshKey = parseParam(paramMap, "SshKey", false, null); + p.sshExecTimeout = Long.parseLong(parseParam(paramMap, "ExecTimeout", false, Long.toString(dEF_timeout))); + p.sshWithRetry = Boolean.valueOf(parseParam(paramMap, "Retry", false, "false")); + p.cmd = parseParam(paramMap, "Cmd", true, null); + p.responsePrefix = parseParam(paramMap, SSH_ResponsePrefix, false, null); + p.responseType = Format.fromString(parseParam(paramMap, "ResponseType", false, "none")); + p.listNameList = getListNameList(paramMap); + p.convertResponse = Boolean.valueOf(parseParam(paramMap, "ConvertResponse", false, "true")); + p.authtype = AuthType.fromString(parseParam(paramMap, "AuthType", false, "unspecified")); + + return p; + } + + public String getStringParameters(Map<String, String> paramMap, String paramName) throws SvcLogicException { + return parseParam(paramMap, SSH_ResponsePrefix, false, null); + } + + public void parseOutput (SvcLogicContext ctx, String outMessage) throws SvcLogicException { + if (p.convertResponse) { + if (p.responseType == Format.NONE) { + classifyOutput(ctx, outMessage); + } else if (p.responseType == Format.JSON) { + classifyOutput(ctx, outMessage); + } else if (p.responseType == Format.XML) { + classifyOutput(ctx, outMessage, p.listNameList); + } + } + } + + private void classifyOutput(SvcLogicContext ctx, String outMessage, Set<String> listNameList) throws SvcLogicException { + Map<String, String> mm = XmlParser.convertToProperties(outMessage, p.listNameList); + toCtx (ctx, mm); + } + + private void toCtx (SvcLogicContext ctx, Map<String, String> mm) { + if (mm != null) { + for (Map.Entry<String, String> entry : mm.entrySet()) { + if (p.responsePrefix != null) { + ctx.setAttribute(p.responsePrefix + "." + entry.getKey(), entry.getValue()); + log.info("+++ " + p.responsePrefix + "." + entry.getKey() + ": [" + entry.getValue() + "]"); + } else { + ctx.setAttribute(entry.getKey(), entry.getValue()); + log.info("+++ " + entry.getKey() + ": [" + entry.getValue() + "]"); + } + } + } + } + + private void classifyOutput(SvcLogicContext ctx, String outMessage) throws SvcLogicException { + try { + Map<String, String> mm = JsonParser.convertToProperties(outMessage); + toCtx (ctx, mm); + } catch (org.codehaus.jettison.json.JSONException e) { + log.info("Output not in JSON format"); + putToProperties(ctx, p.responsePrefix); + } catch (Exception e) { + throw new SvcLogicException("error parsing response file"); + } + } + + private void putToProperties(SvcLogicContext ctx, String outMessage) throws SvcLogicException { + + try { + Properties prop = new Properties(); + prop.load(new ByteArrayInputStream(outMessage.getBytes(StandardCharsets.UTF_8))); + for (Object key : prop.keySet()) { + String name = (String) key; + String value = prop.getProperty(name); + if (value != null && value.trim().length() > 0) { + if (p.responsePrefix != null) { + ctx.setAttribute(p.responsePrefix + "." + name, value.trim()); + log.info("+++ " + p.responsePrefix + "." + name + ": [" + value + "]"); + } else { + ctx.setAttribute(name, value.trim()); + log.info("+++ " + name + ": [" + value + "]"); + } + } + } + } catch (Exception e) { + throw new SvcLogicException( "Error parsing response file."); + } + } + + public String makeCommand (Map<String, String> params) { + JSONObject jsonPayload = new JSONObject(); + final String[] optionalTestParams = {ENV_PARAMETERS_OPT_KEY, FILE_PARAMETERS_OPT_KEY}; + parseParam(params, optionalTestParams, jsonPayload); + JSONObject envParams = (JSONObject) jsonPayload.remove(ENV_PARAMETERS_OPT_KEY); + JSONObject fileParams = (JSONObject) jsonPayload.remove(FILE_PARAMETERS_OPT_KEY); + + StringBuilder constructedCommand = new StringBuilder(); + constructedCommand.append(parseFileParam(fileParams)).append(p.cmd).append(" ").append(parseEnvParam(envParams)); + + return constructedCommand.toString(); + } + + private String parseEnvParam(JSONObject envParams) { + StringBuilder envParamBuilder = new StringBuilder(); + if (envParams != null) { + for (Object key : envParams.keySet()) { + if (envParamBuilder.length() > 0) { + envParamBuilder.append(", "); + } + envParamBuilder.append(key + "=" + envParams.get((String) key)); + } + } + return envParamBuilder.toString(); + } + + private String parseFileParam(JSONObject fileParams) { + StringBuilder fileParamBuilder = new StringBuilder(); + if (fileParams != null) { + for (Object key : fileParams.keySet()) { + fileParamBuilder.append("echo -e \"" + fileParams.get((String) key) + "\" > /srv/salt/" + key).append("; "); + } + } + return fileParamBuilder.toString(); + } + + private void parseParam(Map<String, String> params, String[] optionalTestParams, JSONObject jsonPayload) + throws JSONException { + + Set<String> optionalParamsSet = new HashSet<>(); + Collections.addAll(optionalParamsSet, optionalTestParams); + + //@formatter:off + params.entrySet() + .stream() + .filter(entry -> optionalParamsSet.contains(entry.getKey())) + .filter(entry -> !Strings.isNullOrEmpty(entry.getValue())) + .forEach(entry -> parseParam(entry, jsonPayload)); + //@formatter:on + } + + private void parseParam(Map.Entry<String, String> params, JSONObject jsonPayload) + throws JSONException { + String key = params.getKey(); + String payload = params.getValue(); + + switch (key) { + case ENV_PARAMETERS_OPT_KEY: + JSONObject paramsJson = new JSONObject(payload); + jsonPayload.put(key, paramsJson); + break; + + case FILE_PARAMETERS_OPT_KEY: + jsonPayload.put(key, getFilePayload(payload)); + break; + + default: + break; + } + } + + /** + * Return payload with escaped newlines + */ + private JSONObject getFilePayload(String payload) { + String formattedPayload = payload.replace("\n", "\\n").replace("\r", "\\r"); + return new JSONObject(formattedPayload); + } + + private Set<String> getListNameList(Map<String, String> paramMap) { + Set<String> ll = new HashSet<>(); + for (Map.Entry<String,String> entry : paramMap.entrySet()) + if (entry.getKey().startsWith("listName")) + ll.add(entry.getValue()); + return ll; + } + + private String parseParam(Map<String, String> paramMap, String name, boolean required, String def) + throws SvcLogicException { + String s = paramMap.get(name); + + if (s == null || s.trim().length() == 0) { + if (!required) + return def; + throw new SvcLogicException("Parameter " + name + " is required in sshapiCallNode"); + } + + s = s.trim(); + StringBuilder value = new StringBuilder(); + int i = 0; + int i1 = s.indexOf('%'); + while (i1 >= 0) { + int i2 = s.indexOf('%', i1 + 1); + if (i2 < 0) + break; + + String varName = s.substring(i1 + 1, i2); + String varValue = System.getenv(varName); + if (varValue == null) + varValue = "%" + varName + "%"; + + value.append(s.substring(i, i1)); + value.append(varValue); + + i = i2 + 1; + i1 = s.indexOf('%', i); + } + value.append(s.substring(i)); + + log.info("Parameter {}: [{}]", name, value); + return value.toString(); + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlJsonUtil.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlJsonUtil.java new file mode 100644 index 000000000..d68fbc6ac --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlJsonUtil.java @@ -0,0 +1,389 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class XmlJsonUtil { + + private static final Logger log = LoggerFactory.getLogger(XmlJsonUtil.class); + + private XmlJsonUtil() { + // Preventing instantiation of the same. + } + + public static String getXml(Map<String, String> varmap, String var) { + boolean escape = true; + if (var.startsWith("'")) { + var = var.substring(1); + escape = false; + } + + Object o = createStructure(varmap, var); + return generateXml(o, 0, escape); + } + + public static String getJson(Map<String, String> varmap, String var) { + boolean escape = true; + if (var.startsWith("'")) { + var = var.substring(1); + escape = false; + } + + boolean quotes = true; + if (var.startsWith("\"")) { + var = var.substring(1); + quotes = false; + } + + Object o = createStructure(varmap, var); + return generateJson(o, escape, quotes); + } + + private static Object createStructure(Map<String, String> flatmap, String var) { + if (flatmap.containsKey(var)) { + if (var.endsWith("_length") || var.endsWith("].key")) + return null; + return flatmap.get(var); + } + + Map<String, Object> mm = new HashMap<>(); + for (String k : flatmap.keySet()) + if (k.startsWith(var + ".")) { + int i1 = k.indexOf('.', var.length() + 1); + int i2 = k.indexOf('[', var.length() + 1); + int i3 = k.length(); + if (i1 > 0 && i1 < i3) + i3 = i1; + if (i2 > 0 && i2 < i3) + i3 = i2; + String k1 = k.substring(var.length() + 1, i3); + String var1 = k.substring(0, i3); + if (!mm.containsKey(k1)) { + Object str = createStructure(flatmap, var1); + if (str != null && (!(str instanceof String) || ((String) str).trim().length() > 0)) + mm.put(k1, str); + } + } + if (!mm.isEmpty()) + return mm; + + boolean arrayFound = false; + for (String k : flatmap.keySet()) + if (k.startsWith(var + "[")) { + arrayFound = true; + break; + } + + if (arrayFound) { + List<Object> ll = new ArrayList<>(); + + int length = Integer.MAX_VALUE; + String lengthStr = flatmap.get(var + "_length"); + if (lengthStr != null) { + try { + length = Integer.parseInt(lengthStr); + } catch (Exception e) { + log.warn("Invalid number for {}_length:{}", var, lengthStr, e); + } + } + + for (int i = 0; i < length; i++) { + Object v = createStructure(flatmap, var + '[' + i + ']'); + if (v == null) + break; + ll.add(v); + } + + if (!ll.isEmpty()) + return ll; + } + + return null; + } + + @SuppressWarnings("unchecked") + private static String generateXml(Object o, int indent, boolean escape) { + if (o == null) + return null; + + if (o instanceof String) + return escape ? escapeXml((String) o) : (String) o;; + + if (o instanceof Map) { + StringBuilder ss = new StringBuilder(); + Map<String, Object> mm = (Map<String, Object>) o; + for (Map.Entry<String, Object> entry: mm.entrySet()) { + Object v = entry.getValue(); + String key = entry.getKey(); + if (v instanceof String) { + String s = escape ? escapeXml((String) v) : (String) v; + ss.append(pad(indent)).append('<').append(key).append('>'); + ss.append(s); + ss.append("</").append(key).append('>').append('\n'); + } else if (v instanceof Map) { + ss.append(pad(indent)).append('<').append(key).append('>').append('\n'); + ss.append(generateXml(v, indent + 1, escape)); + ss.append(pad(indent)).append("</").append(key).append('>').append('\n'); + } else if (v instanceof List) { + List<Object> ll = (List<Object>) v; + for (Object o1 : ll) { + ss.append(pad(indent)).append('<').append(key).append('>').append('\n'); + ss.append(generateXml(o1, indent + 1, escape)); + ss.append(pad(indent)).append("</").append(key).append('>').append('\n'); + } + } + } + return ss.toString(); + } + + return null; + } + + private static String generateJson(Object o, boolean escape, boolean quotes) { + if (o == null) + return null; + + StringBuilder ss = new StringBuilder(); + generateJson(ss, o, 0, false, escape, quotes); + return ss.toString(); + } + + @SuppressWarnings("unchecked") + private static void generateJson(StringBuilder ss, Object o, int indent, boolean padFirst, boolean escape, boolean quotes) { + if (o instanceof String) { + String s = escape ? escapeJson((String) o) : (String) o; + if (padFirst) + ss.append(pad(indent)); + if (quotes) { + ss.append('"').append(s).append('"'); + } else { + ss.append(s); + } + return; + } + + if (o instanceof Map) { + Map<String, Object> mm = (Map<String, Object>) o; + + if (padFirst) + ss.append(pad(indent)); + ss.append("{\n"); + + boolean first = true; + for (Map.Entry<String, Object> entry : mm.entrySet()) { + if (!first) + ss.append(",\n"); + first = false; + Object v = entry.getValue(); + String key = entry.getKey(); + ss.append(pad(indent + 1)).append('"').append(key).append("\": "); + generateJson(ss, v, indent + 1, false, escape, true); + } + + ss.append("\n"); + ss.append(pad(indent)).append('}'); + + return; + } + + if (o instanceof List) { + List<Object> ll = (List<Object>) o; + + if (padFirst) + ss.append(pad(indent)); + ss.append("[\n"); + + boolean first = true; + for (Object o1 : ll) { + if (!first) + ss.append(",\n"); + first = false; + + generateJson(ss, o1, indent + 1, true, escape, quotes); + } + + ss.append("\n"); + ss.append(pad(indent)).append(']'); + } + } + + public static String removeLastCommaJson(String s) { + StringBuilder sb = new StringBuilder(); + int k = 0; + int start = 0; + while (k < s.length()) { + int i11 = s.indexOf('}', k); + int i12 = s.indexOf(']', k); + int i1 = -1; + if (i11 < 0) + i1 = i12; + else if (i12 < 0) + i1 = i11; + else + i1 = i11 < i12 ? i11 : i12; + if (i1 < 0) + break; + + int i2 = s.lastIndexOf(',', i1); + if (i2 < 0) { + k = i1 + 1; + continue; + } + + String between = s.substring(i2 + 1, i1); + if (between.trim().length() > 0) { + k = i1 + 1; + continue; + } + + sb.append(s.substring(start, i2)); + start = i2 + 1; + k = i1 + 1; + } + + sb.append(s.substring(start, s.length())); + + return sb.toString(); + } + + public static String removeEmptyStructJson(String s) { + int k = 0; + while (k < s.length()) { + boolean curly = true; + int i11 = s.indexOf('{', k); + int i12 = s.indexOf('[', k); + int i1 = -1; + if (i11 < 0) { + i1 = i12; + curly = false; + } else if (i12 < 0) + i1 = i11; + else + if (i11 < i12) + i1 = i11; + else { + i1 = i12; + curly = false; + } + + if (i1 >= 0) { + int i2 = curly ? s.indexOf('}', i1) : s.indexOf(']', i1); + if (i2 > 0) { + String value = s.substring(i1 + 1, i2); + if (value.trim().length() == 0) { + int i4 = s.lastIndexOf('\n', i1); + if (i4 < 0) + i4 = 0; + int i5 = s.indexOf('\n', i2); + if (i5 < 0) + i5 = s.length(); + + s = s.substring(0, i4) + s.substring(i5); + k = 0; + } else + k = i1 + 1; + } else + break; + } else + break; + } + + return s; + } + + public static String removeEmptyStructXml(String s) { + int k = 0; + while (k < s.length()) { + int i1 = s.indexOf('<', k); + if (i1 < 0 || i1 == s.length() - 1) + break; + + char c1 = s.charAt(i1 + 1); + if (c1 == '?' || c1 == '!') { + k = i1 + 2; + continue; + } + + int i2 = s.indexOf('>', i1); + if (i2 < 0) { + k = i1 + 1; + continue; + } + + String closingTag = "</" + s.substring(i1 + 1, i2 + 1); + int i3 = s.indexOf(closingTag, i2 + 1); + if (i3 < 0) { + k = i2 + 1; + continue; + } + + String value = s.substring(i2 + 1, i3); + if (value.trim().length() > 0) { + k = i2 + 1; + continue; + } + + int i4 = s.lastIndexOf('\n', i1); + if (i4 < 0) + i4 = 0; + int i5 = s.indexOf('\n', i3); + if (i5 < 0) + i5 = s.length(); + + s = s.substring(0, i4) + s.substring(i5); + k = 0; + } + + return s; + } + + private static String escapeXml(String v) { + String s = v.replaceAll("&", "&"); + s = s.replaceAll("<", "<"); + s = s.replaceAll("'", "'"); + s = s.replaceAll("\"", """); + s = s.replaceAll(">", ">"); + return s; + } + + private static String escapeJson(String v) { + String s = v.replaceAll("\\\\", "\\\\\\\\"); + s = s.replaceAll("\"", "\\\\\""); + return s; + } + + private static String pad(int n) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < n; i++) + s.append(Character.toString('\t')); + return s.toString(); + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlParser.java b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlParser.java new file mode 100644 index 000000000..6ea770ad9 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/sshapicall/model/XmlParser.java @@ -0,0 +1,176 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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.ccsdk.sli.plugins.sshapicall.model; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +public final class XmlParser { + + private static final Logger log = LoggerFactory.getLogger(XmlParser.class); + + private XmlParser() { + // Preventing instantiation of the same. + } + + public static Map<String, String> convertToProperties(String s, Set<String> listNameList) + throws SvcLogicException { + + checkNotNull(s, "Input should not be null."); + + Handler handler = new Handler(listNameList); + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser saxParser = factory.newSAXParser(); + InputStream in = new ByteArrayInputStream(s.getBytes()); + saxParser.parse(in, handler); + } catch (ParserConfigurationException | IOException | SAXException | NumberFormatException e) { + throw new SvcLogicException("Unable to convert XML to properties" + e.getLocalizedMessage(), e); + } + return handler.getProperties(); + } + + private static class Handler extends DefaultHandler { + + private Set<String> listNameList; + + private Map<String, String> properties = new HashMap<>(); + + StringBuilder currentName = new StringBuilder(); + StringBuilder currentValue = new StringBuilder(); + + public Handler(Set<String> listNameList) { + super(); + this.listNameList = listNameList; + if (this.listNameList == null) + this.listNameList = new HashSet<>(); + } + + public Map<String, String> getProperties() { + return properties; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + super.startElement(uri, localName, qName, attributes); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + if (currentName.length() > 0) + currentName.append(Character.toString('.')); + currentName.append(name); + + String listName = removeIndexes(currentName.toString()); + + if (listNameList.contains(listName)) { + String n = currentName.toString() + "_length"; + int len = getInt(properties, n); + properties.put(n, String.valueOf(len + 1)); + currentName.append("[").append(len).append("]"); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + + String name = localName; + if (name == null || name.trim().length() == 0) + name = qName; + int i2 = name.indexOf(':'); + if (i2 >= 0) + name = name.substring(i2 + 1); + + String s = currentValue.toString().trim(); + if (s.length() > 0) { + properties.put(currentName.toString(), s); + + log.info("Added property: {} : {}", currentName, s); + currentValue = new StringBuilder(); + } + + int i1 = currentName.lastIndexOf("." + name); + if (i1 <= 0) + currentName = new StringBuilder(); + else + currentName = new StringBuilder(currentName.substring(0, i1)); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + + String value = new String(ch, start, length); + currentValue.append(value); + } + + private static int getInt(Map<String, String> mm, String name) { + String s = mm.get(name); + if (s == null) + return 0; + return Integer.parseInt(s); + } + + private String removeIndexes(String currentName) { + StringBuilder b = new StringBuilder(); + boolean add = true; + for (int i = 0; i < currentName.length(); i++) { + char c = currentName.charAt(i); + if (c == '[') + add = false; + else if (c == ']') + add = true; + else if (add) + b.append(Character.toString(c)); + } + return b.toString(); + } + } +} diff --git a/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml b/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml new file mode 100644 index 000000000..50c9b2aa3 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-context.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> + <property name="locations"> + <list> + <value>file:${SDNC_CONFIG_DIR}/ueb.properties</value> + <value>file:${SDNC_CONFIG_DIR}/dme2.properties</value> + </list> + </property> + <property name="ignoreResourceNotFound" value="true" /> + <property name="ignoreUnresolvablePlaceholders" value="true" /> + </bean> + + <!-- context:property-placeholder location="file:${SDNC_CONFIG_DIR}/ueb.properties" /--> + + <bean id="sshapiCallNode" class="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode"> + <property name="uebServers" value="${servers}" /> + </bean> + +</beans> diff --git a/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml b/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml new file mode 100644 index 000000000..25631d6af --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/resources/META-INF/spring/sshapi-call-node-osgi-context.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<beans:beans xmlns="http://www.springframework.org/schema/osgi" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" + xsi:schemaLocation="http://www.springframework.org/schema/osgi + http://www.springframework.org/schema/osgi/spring-osgi.xsd + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <service ref="sshapiCallNode" interface="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode" /> + +</beans:beans> diff --git a/plugins/sshapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/sshapi-call-node-blueprint.xml b/plugins/sshapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/sshapi-call-node-blueprint.xml new file mode 100755 index 000000000..7a5424329 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/resources/OSGI-INF/blueprint/sshapi-call-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="sshapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode" /> + + + + <service ref="sshapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml b/plugins/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml new file mode 100755 index 000000000..7a5424329 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/main/resources/org/opendaylight/blueprint/sshapi-call-node-blueprint.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0" + odl:use-default-for-reference-types="true"> + + <bean id="sshapiCallNodeProvider" class="org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode" /> + + + + <service ref="sshapiCallNodeProvider"> + <interfaces> + <value>org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode</value> + </interfaces> + </service> + +</blueprint>
\ No newline at end of file diff --git a/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java new file mode 100644 index 000000000..78f20e327 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestJsonParser.java @@ -0,0 +1,85 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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 jtest.org.onap.ccsdk.sli.plugins.restapicall; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.codehaus.jettison.json.JSONException; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.sshapicall.model.JsonParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestJsonParser { + + private static final Logger log = LoggerFactory.getLogger(TestJsonParser.class); + + @Test + public void test() throws SvcLogicException, IOException { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test.json")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + Map<String, String> mm = null; + try { + mm = JsonParser.convertToProperties(b.toString()); + } catch (JSONException e){ + throw new SvcLogicException(e.getMessage()); + } + + logProperties(mm); + + in.close(); + } + + @Test(expected = NullPointerException.class) + public void testNullString() throws SvcLogicException { + Map<String, String> mm = null; + try { + mm = JsonParser.convertToProperties(null); + } catch (JSONException e){ + throw new SvcLogicException(e.getMessage()); + } + } + + private void logProperties(Map<String, String> mm) { + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) + ll.add((String) o); + Collections.sort(ll); + log.info("Properties:"); + for (String name : ll) + log.info("--- {}: {}", name, mm.get(name)); + } +} diff --git a/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestSshApiCallNode.java b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestSshApiCallNode.java new file mode 100644 index 000000000..c0bcdb82a --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestSshApiCallNode.java @@ -0,0 +1,330 @@ +package jtest.org.onap.ccsdk.sli.plugins.restapicall; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.sshapicall.SshApiCallNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class TestSshApiCallNode { + + private static final Logger log = LoggerFactory.getLogger(TestSshApiCallNode.class); + + private SshApiCallNode adapter; + private String TestId; + private boolean testMode = true; + private Map<String, String> params; + private SvcLogicContext svcContext; + + + @Before + public void setup() throws IllegalArgumentException { + testMode = true; + svcContext = new SvcLogicContext(); + adapter = new SshApiCallNode(); + + params = new HashMap<>(); + params.put("AgentUrl", "https://192.168.1.1"); + params.put("User", "test"); + params.put("Password", "test"); + } + + @After + public void tearDown() { + testMode = false; + adapter = null; + params = null; + svcContext = null; + } + + @Test(expected = SvcLogicException.class) + public void testExecCommand_noUrlFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("HostName", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execCommand(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandPty_noUrlFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("HostName", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execCommandWithPty(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noUrlFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("HostName", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommand_noPortFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execCommand(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandPty_noPortFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execCommandWithPty(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noPortFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Test", "fail"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommand_noCmdFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + adapter.execCommand(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandPty_noCmdFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + adapter.execCommandWithPty(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noCmdFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noSSHBasicFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("AuthType", "basic"); + params.put("Cmd", "test"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noSSHKeyFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("AuthType", "key"); + params.put("Cmd", "test"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noSSHNoneFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("AuthType", "none"); + params.put("Cmd", "test"); + params.put("ResponseType", "xml"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_noSSHFailed() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("ResponseType", "json"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = IllegalArgumentException.class) + public void testExecCommandResponse_noSSHInvalidParam() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("ResponseType", "txt"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = IllegalArgumentException.class) + public void testExecCommandResponse_noSSHInvalidAuthParam() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "spring"); + params.put("ResponseType", "json"); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test + public void testExecCommandResponse_validJSON() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "basic"); + params.put("ResponseType", "json"); + params.put("TestOut", "{\"equipment-data\":\"boo\"}"); + params.put("TestFail", "false"); + adapter = new SshApiCallNode(true); + adapter.execWithStatusCheck(params, svcContext); + assertEquals("boo", svcContext.getAttribute("equipment-data")); + } + + @Test + public void testExecCommandResponse_validXML() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "basic"); + params.put("ResponseType", "xml"); + params.put("TestOut", "<modelVersion>4.0.0</modelVersion>"); + params.put("TestFail", "false"); + adapter = new SshApiCallNode(true); + adapter.execWithStatusCheck(params, svcContext); + assertEquals("4.0.0", svcContext.getAttribute("modelVersion")); + } + + @Test + public void testExecCommandResponse_validJSONPrefix() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "basic"); + params.put("ResponseType", "json"); + params.put("TestOut", "{\"equipment-data\":\"boo\"}"); + params.put("ResponsePrefix", "test"); + params.put("TestFail", "false"); + adapter = new SshApiCallNode(true); + adapter.execWithStatusCheck(params, svcContext); + assertEquals("boo", svcContext.getAttribute("test.equipment-data")); + } + + @Test + public void testExecCommandResponse_validXMLPrefix() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "basic"); + params.put("ResponseType", "xml"); + params.put("TestOut", "<modelVersion>4.0.0</modelVersion>"); + params.put("ResponsePrefix", "test"); + params.put("TestFail", "false"); + adapter = new SshApiCallNode(true); + adapter.execWithStatusCheck(params, svcContext); + assertEquals("4.0.0", svcContext.getAttribute("test.modelVersion")); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_validXMLFail() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + + params.put("Url", "test"); + params.put("Port", "10"); + params.put("User", "test"); + params.put("Password", "test"); + params.put("Cmd", "test"); + params.put("AuthType", "basic"); + params.put("ResponseType", "xml"); + params.put("TestOut", "<modelVersion>4.0.0</modelVersion>"); + params.put("TestFail", "true"); + params.put("ResponsePrefix", "test"); + adapter = new SshApiCallNode(true); + adapter.execWithStatusCheck(params, svcContext); + } + + @Test(expected = SvcLogicException.class) + public void testExecCommandResponse_validXMLPrefixKey() throws SvcLogicException, + IllegalStateException, IllegalArgumentException { + params = new HashMap<>(); + params.put("Url", "test"); + params.put("Port", "10"); + params.put("SshKey", "test"); + params.put("Cmd", "test"); + params.put("ResponseType", "xml"); + params.put("TestOut", "<modelVersion>4.0.0</modelVersion>"); + params.put("ResponsePrefix", "test"); + adapter.execWithStatusCheck(params, svcContext); + assertEquals("4.0.0", svcContext.getAttribute("test.modelVersion")); + } +} diff --git a/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java new file mode 100644 index 000000000..33e0f24dd --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlJsonUtil.java @@ -0,0 +1,264 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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 jtest.org.onap.ccsdk.sli.plugins.restapicall; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.onap.ccsdk.sli.plugins.sshapicall.model.XmlJsonUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class TestXmlJsonUtil { + + private static final Logger log = LoggerFactory.getLogger(TestXmlJsonUtil.class); + + @Test + public void test() { + Map<String, String> mm = new HashMap<>(); + mm.put("service-data.vnf-config-parameters-list.vnf-config-parameters[0].vnf-type", "N-SBG"); + mm.put("service-data.service-information.service-instance-id", "someinstance001"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-server-ip-address", "10.11.12.13"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].escf-domain-name", "hclab.atttest.com"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3_length", "2"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-target-v3-id", "1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-target-ip-address", "127.0.0.1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[0].snmp-security-level", "NO_AUTH_NO_PRIV"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-target-v3-id", "2"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-target-ip-address", "192.168.1.8"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].snmp-target-v3[1].snmp-security-level", "NO_AUTH_NO_PRIV"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-ip-address-1", "2001:1890:1001:2224::1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].dns-ip-address-2", "2001:1890:1001:2424::1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].diameter-rf-realm-name", "uvp.els-an.att.net"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].diameter-rf-peer-ip-address", "192.168.1.66"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].bgf-controller-ip-address", "192.168.1.186"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].bgf-control-link-name", "mg3/69@192.168.1.226"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].rf-interface-nexthop-ip-address", "10.111.108.150"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].rf-mated-pair-ip-address", "10.111.108.146"); + + mm.put("service-data.vnf-config-parameters-list.vnf-config-parameters[0].access-net-pcscf_length", "4"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[0].network-name", "UvpbUgnAccess1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[0].sip-pa-termination-ip-address", "10.111.108.146"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].network-name", "MIS"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].next-hop-ip-address", "10.111.108.158"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[1].subnet-mask-length", "10.111.108.154"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].network-name", "AVPN1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].next-hop-ip-address", "10.111.108.166"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[2].subnet-mask-length", "10.111.108.162"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].network-name", "AVPN1"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].proactive-transcoding-profile", + "trinity-transcodingProfile"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].next-hop-ip-address", "10.129.108.166"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].access-net-pcscf[3].subnet-mask-length", "10.129.108.162"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf_length", "1"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].network-name", "Core"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].next-hop-ip-address", "10.111.108.142"); + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].core-net-pcscf[0].sip-pa-termination-ip-address", "10.111.108.138"); + + mm.put("service-data.vnf-config-parameters-list." + + "vnf-config-parameters[0].mated-pair-fully-qualified-domain-name", + "mt1nj01sbg01pyl-mt1nj01sbg02pyl.ar1ga.uvp.els-an.att.net"); + + mm.put("service-data.appc-request-header.svc-request-id", "SOMESERVICEREQUEST123451000"); + mm.put("service-data.vnf-config-information.vnf-host-ip-address", "192.168.13.151"); + mm.put("service-data.vnf-config-information.vendor", "Netconf"); + + mm.put("service-data.vnf-config-information.escape-test", + "blah blah \"xxx&nnn<>\\'\"there>blah<&''\"\"123\\\\\\'''blah blah &"); + + String ss = XmlJsonUtil.getXml(mm, "service-data.vnf-config-parameters-list"); + log.info(ss); + + ss = XmlJsonUtil.getXml(mm, "service-data.vnf-config-information"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-parameters-list.vnf-config-parameters"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-information"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "service-data.vnf-config-information.vnf-host-ip-address"); + log.info(ss); + + ss = XmlJsonUtil.getJson(mm, "\"service-data.vnf-config-information.vnf-host-ip-address"); + log.info(ss); + } + + @Test + public void testRemoveEmptyStructXml() { + String xmlin = "" + + "<T1>\n" + + " <T2>\n" + + " <T3>\n" + + " <T4></T4>\n" + + " <T5> </T5>\n" + + " <T6>\n" + + " </T6>\n" + + " </T3>\n" + + " <T7>blah</T7>\n" + + " </T2>\n" + + " <T8>\n" + + " <T9>\n" + + " <T10></T10>\n" + + " <T11> </T11>\n" + + " <T12>\n" + + " </T12>\n" + + " </T9>\n" + + " <T13>\n" + + " <T14></T14>\n" + + " <T15> </T15>\n" + + " <T16>\n" + + " <T17></T17>\n" + + " </T16>\n" + + " </T13>\n" + + " <T17>\n" + + " </T17>\n" + + " </T8>\n" + + " <T18>blah blah</T18>\n" + + "</T1>\n"; + + String xmloutexpected = "" + + "<T1>\n" + + " <T2>\n" + + " <T7>blah</T7>\n" + + " </T2>\n" + + " <T18>blah blah</T18>\n" + + "</T1>\n"; + + String xmlout = XmlJsonUtil.removeEmptyStructXml(xmlin); + log.info(xmlout); + + Assert.assertEquals(xmloutexpected, xmlout); + } + + @Test + public void testRemoveEmptyStructJson() { + String xmlin = "{\r\n" + + " \"T1\":{\r\n" + + " \"T2\":{\r\n" + + " \"T3\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T4\":{\r\n" + + " \"T12\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T13\":[ ],\r\n" + + " \"T14\":{\r\n" + + " \"T15\":{\r\n" + + " \r\n" + + " },\r\n" + + " \"T16\":{\r\n" + + " \r\n" + + " }\r\n" + + " }\r\n" + + " },\r\n" + + " \"T5\":{\r\n" + + " \"T6\":[\r\n" + + " \r\n" + + " ],\r\n" + + " \"T7\":[\r\n" + + " \"T8\":{\r\n" + + " \r\n" + + " },\r\n" + + " \"T9\":{ },\r\n" + + " \"T10\":\"blah\",\r\n" + + " \"T11\":[\r\n" + + " \r\n" + + " ]\r\n" + + " ]\r\n" + + " }\r\n" + + " }\r\n" + + " }\r\n" + + "}\r\n" + + ""; + + String xmloutexpected = "{\r\n" + + " \"T1\":{\r\n" + + " \"T2\":{\r\n" + + " \"T5\":{\r\n" + + " \"T7\":[\r\n" + + " \"T10\":\"blah\",\r\n" + + " ]\r\n" + + " }\r\n" + + " }\r\n" + + " }\r\n" + + "}\r\n" + + ""; + + String xmlout = XmlJsonUtil.removeEmptyStructJson(xmlin); + log.info(xmlout); + + Assert.assertEquals(xmloutexpected, xmlout); + } +} diff --git a/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java new file mode 100644 index 000000000..ba0ae41de --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/java/jtest/org/onap/ccsdk/sli/plugins/restapicall/TestXmlParser.java @@ -0,0 +1,123 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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 jtest.org.onap.ccsdk.sli.plugins.restapicall; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.plugins.sshapicall.model.XmlParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestXmlParser { + + private static final Logger log = LoggerFactory.getLogger(TestXmlParser.class); + + @Test + public void test() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test3.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("project.dependencies.dependency"); + listNameList.add("project.build.plugins.plugin"); + listNameList.add("project.build.plugins.plugin.executions.execution"); + listNameList.add("project.build.pluginManagement.plugins.plugin"); + listNameList.add("project.build.pluginManagement." + + "plugins.plugin.configuration.lifecycleMappingMetadata.pluginExecutions.pluginExecution"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + logProperties(mm); + in.close(); + } + + @Test + public void testValidLength() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("test3.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + + assertThat(mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport[5]"), is("SET_RESET_LP")); + assertThat(mm.get("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport[0]"), is("SET_BVOIP_IN")); + + logProperties(mm); + in.close(); + } + + @Test(expected = SvcLogicException.class) + public void testInvalidLength() throws Exception { + BufferedReader in = new BufferedReader( + new InputStreamReader(ClassLoader.getSystemResourceAsStream("invalidlength.xml")) + ); + StringBuilder b = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) + b.append(line).append('\n'); + + Set<String> listNameList = new HashSet<String>(); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfImport"); + listNameList.add("ApplyGroupResponse.ApplyGroupResponseData.VrfDetails.VrfExport"); + + Map<String, String> mm = XmlParser.convertToProperties(b.toString(), listNameList); + logProperties(mm); + in.close(); + } + + private void logProperties(Map<String, String> mm) { + List<String> ll = new ArrayList<>(); + for (Object o : mm.keySet()) + ll.add((String) o); + Collections.sort(ll); + + log.info("Properties:"); + for (String name : ll) + log.info("--- " + name + ": " + mm.get(name)); + } +} diff --git a/plugins/sshapi-call-node/provider/src/test/resources/invalidlength.xml b/plugins/sshapi-call-node/provider/src/test/resources/invalidlength.xml new file mode 100644 index 000000000..b71f6391f --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/resources/invalidlength.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport_length>a</VrfExport_length> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/sshapi-call-node/provider/src/test/resources/test-template.json b/plugins/sshapi-call-node/provider/src/test/resources/test-template.json new file mode 100644 index 000000000..cd5e74699 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/resources/test-template.json @@ -0,0 +1,57 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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========================================================= + */ + +{ + "sdn-circuit-request": [ + ${repeat:tmp.sdn-circuit-req-row_length: + { + "request-id": ${tmp.sdn-circuit-req-row[${1}].request-id}, + "source-uid": ${tmp.sdn-circuit-req-row[${1}].source-uid}, + "action": ${tmp.sdn-circuit-req-row[${1}].action}, + "request-timestamp": ${tmp.sdn-circuit-req-row[${1}].request-timestamp}, + "update-timestamp": ${tmp.sdn-circuit-req-row[${1}].update-timestamp}, + "request-status": ${tmp.sdn-circuit-req-row[${1}].request-status}, + "processing-status": ${tmp.sdn-circuit-req-row[${1}].processing-status}, + "reason-code": ${tmp.sdn-circuit-req-row[${1}].reason-code}, + "reason-message": ${tmp.sdn-circuit-req-row[${1}].reason-message}, + "customer-code": ${tmp.sdn-circuit-req-row[${1}].customer-code}, + "bundle-id": ${tmp.sdn-circuit-req-row[${1}].bundle-id}, + "router-name-1": ${tmp.sdn-circuit-req-row[${1}].router-name-1}, + "tail-clfi-1": ${tmp.sdn-circuit-req-row[${1}].tail-clfi-1}, + "srg-1": ${tmp.sdn-circuit-req-row[${1}].srg-1}, + "router-name-2": ${tmp.sdn-circuit-req-row[${1}].router-name-2}, + "tail-clfi-2": ${tmp.sdn-circuit-req-row[${1}].tail-clfi-2}, + "srg-2": ${tmp.sdn-circuit-req-row[${1}].srg-2}, + "facility-speed": ${tmp.sdn-circuit-req-row[${1}].facility-speed}, + "facility-speed-units": ${tmp.sdn-circuit-req-row[${1}].facility-speed-units}, + "facility-type": ${tmp.sdn-circuit-req-row[${1}].facility-type}, + "service-clfi": ${tmp.sdn-circuit-req-row[${1}].service-clfi}, + "clci": ${tmp.sdn-circuit-req-row[${1}].clci}, + "wavelength-purpose": ${tmp.sdn-circuit-req-row[${1}].wavelength-purpose}, + "activate-setting": ${tmp.sdn-circuit-req-row[${1}].activate-setting}, + "apply-date": ${tmp.sdn-circuit-req-row[${1}].apply-date}, + "run-id": ${tmp.sdn-circuit-req-row[${1}].run-id}, + "hostname": ${tmp.sdn-circuit-req-row[${1}].hostname}, + "algo-request-reason": ${tmp.sdn-circuit-req-row[${1}].algo-request-reason} + }, + } + ] +} diff --git a/plugins/sshapi-call-node/provider/src/test/resources/test.json b/plugins/sshapi-call-node/provider/src/test/resources/test.json new file mode 100644 index 000000000..e272d457a --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/resources/test.json @@ -0,0 +1,51 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + * reserved. + * ================================================================================ + * 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========================================================= + */ + +{ + "equipment-data": [ + { + "server-count": "4", + "max-server-speed": "1600000", + "number-primary-servers": "2", + "equipment-id": "Server1", + "server-model": "Unknown", + "server-id": "Server1", + "test-node" : { + "test-inner-node" : "Test-Value" + } + } + ], + "resource-state": { + "threshold-value": "1600000", + "last-added": "1605000", + "used": "1605000", + "limit-value": "1920000" + }, + "resource-rule": { + "endpoint-position": "VCE-Cust", + "soft-limit-expression": "0.6 * max-server-speed * number-primary-servers", + "resource-name": "Bandwidth", + "service-model": "DUMMY", + "hard-limit-expression": "max-server-speed * number-primary-servers", + "equipment-level": "Server" + }, + "message": "The provisioned access bandwidth is at or exceeds 50% of the total server capacity." +} diff --git a/plugins/sshapi-call-node/provider/src/test/resources/test.xml b/plugins/sshapi-call-node/provider/src/test/resources/test.xml new file mode 100644 index 000000000..7645f1992 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/resources/test.xml @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. + * ================================================================================ + * Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>sshapi-call-node</artifactId> + <version>6.0.0-SNAPSHOT</version> + </parent> + <artifactId>sshapi-call-node-provider</artifactId> + <packaging>bundle</packaging> + <name>SSHAPI Call Node - Provider</name> + <url>http://maven.apache.org</url> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>3.1.4.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-common</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli</groupId> + <artifactId>sli-provider</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-beans</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>3.1.4.RELEASE</version> + </dependency> + <dependency> + <groupId>xerces</groupId> + <artifactId>xerces</artifactId> + <version>2.4.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-signature</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>com.sun.jersey.contribs.jersey-oauth</groupId> + <artifactId>oauth-client</artifactId> + <version>1.17</version> + </dependency> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>com.brocade.developer</groupId> + <artifactId>providermodule-plugin</artifactId> + <configuration> + <packageId>org.onap.ccsdk.sli.plugins</packageId> + <appName>sshapi-call-node</appName> + </configuration> + <executions> + <execution> + <phase>process-sources</phase> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>org.onap.ccsdk.sli.plugins.sshapicall</Bundle-SymbolicName> + <Export-Package>org.onap.ccsdk.sli.plugins.sshapicall</Export-Package> + <Import-Package>*</Import-Package> + </instructions> + + <manifestLocation>${project.basedir}/src/main/resources/META-INF</manifestLocation> + + </configuration> + + </plugin> + + + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + com.brocade.developer + </groupId> + <artifactId> + providermodule-plugin + </artifactId> + <versionRange> + [1.2.0.100-SNAPSHOT,) + </versionRange> + <goals> + <goal>process</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/plugins/sshapi-call-node/provider/src/test/resources/test3.xml b/plugins/sshapi-call-node/provider/src/test/resources/test3.xml new file mode 100644 index 000000000..52a7a7d98 --- /dev/null +++ b/plugins/sshapi-call-node/provider/src/test/resources/test3.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2018 Samsung Electronics. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<ApplyGroupResponse xmlns="http://onap.org/vpn/schema/v1" + xmlns:ns2="http://onap.org/prov/vpn/schema/v2"> + <ApplyGroupResponseData> + <ServiceInstanceId>ICOREPVC-81114561</ServiceInstanceId> + <VrfDetails> + <End2EndVpnKey>VPNL811182</End2EndVpnKey> + <VpnId>811182</VpnId> + <VrfName>21302:811182</VrfName> + <VrfImport>SET_BVOIP_IN</VrfImport> + <VrfImport>SET6_BVOIP_IN</VrfImport> + <VrfExport>SET6_DSU</VrfExport> + <VrfExport>SET_DSU</VrfExport> + <VrfExport>SET6_MANAGED</VrfExport> + <VrfExport>SET_MANAGED</VrfExport> + <VrfExport>SET_LOVRF_COMMUNITY</VrfExport> + <VrfExport>SET_RESET_LP</VrfExport> + <ApplyGroup> + <ns2:ApplyGroup>AG_MAX_MCASTROUTES</ns2:ApplyGroup> + </ApplyGroup> + </VrfDetails> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v4</Family> + <PeerGroupName>gp_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + <RoutingApplyGroups> + <RoutingProtocol>BGP4_PROTOCOL</RoutingProtocol> + <Family>v6</Family> + <PeerGroupName>gp6_21302:811182</PeerGroupName> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_L3VPN_EBGP</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupPeer> + <ns2:ApplyGroup>AG6_MAX_PREFIX</ns2:ApplyGroup> + </ApplyGroupPeer> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BGP_UNMANAGED</ns2:ApplyGroup> + </ApplyGroupNeighbour> + <ApplyGroupNeighbour> + <ns2:ApplyGroup>AG6_BFD_BGP_3000</ns2:ApplyGroup> + </ApplyGroupNeighbour> + </RoutingApplyGroups> + </ApplyGroupResponseData> + <response-code>200</response-code> + <response-message>Success</response-message> + <ack-final-indicator>Y</ack-final-indicator> +</ApplyGroupResponse> + diff --git a/plugins/template-node/installer/pom.xml b/plugins/template-node/installer/pom.xml new file mode 100644 index 000000000..c6ed6312f --- /dev/null +++ b/plugins/template-node/installer/pom.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node-installer</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ccsdk-sli-plugins :: template-node :: ${project.artifactId}</name> + + <properties> + <application.name>ccsdk-template-node</application.name> + <features.boot>${application.name}</features.boot> + <features.repositories>mvn:org.onap.ccsdk.sli.plugins/${features.boot}/${project.version}/xml/features</features.repositories> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node-provider</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + <execution> + <id>installer-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${application.name}-${project.version}-installer</finalName> + <descriptors> + <descriptor>src/assembly/assemble_installer_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>false</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeGroupIds>org.onap.ccsdk.sli.plugins</includeGroupIds> + <scope>provided</scope> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>copy-version</id> + <goals> + <goal>copy-resources</goal> + </goals><!-- here the phase you need --> + <phase>validate</phase> + <configuration> + <outputDirectory>${basedir}/target/stage</outputDirectory> + <resources> + <resource> + <directory>src/main/resources/scripts</directory> + <includes> + <include>install-feature.sh</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> + + </executions> + </plugin> + + </plugins> + </build> +</project> diff --git a/plugins/template-node/installer/src/assembly/assemble_installer_zip.xml b/plugins/template-node/installer/src/assembly/assemble_installer_zip.xml new file mode 100644 index 000000000..d7a0a630d --- /dev/null +++ b/plugins/template-node/installer/src/assembly/assemble_installer_zip.xml @@ -0,0 +1,58 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>installer_zip</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level as this + file is suppose to be unzip on top of a karaf distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>755</fileMode> + <includes> + <include>*.sh</include> + </includes> + </fileSet> + <fileSet> + <directory>target/stage/</directory> + <outputDirectory>${application.name}</outputDirectory> + <fileMode>644</fileMode> + <excludes> + <exclude>*.sh</exclude> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/template-node/installer/src/assembly/assemble_mvnrepo_zip.xml b/plugins/template-node/installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..7500304e8 --- /dev/null +++ b/plugins/template-node/installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,48 @@ +<!-- + ============LICENSE_START======================================================= + openECOMP : SDN-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level as this + file is suppose to be unzip on top of a karaf distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/plugins/template-node/installer/src/main/resources/scripts/install-feature.sh b/plugins/template-node/installer/src/main/resources/scripts/install-feature.sh new file mode 100644 index 000000000..98276a7e0 --- /dev/null +++ b/plugins/template-node/installer/src/main/resources/scripts/install-feature.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +### +# ============LICENSE_START======================================================= +# openECOMP : SDN-C +# ================================================================================ +# Copyright (C) 2017 AT&T Intellectual Property. All rights +# reserved. +# ================================================================================ +# 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========================================================= +### + +ODL_HOME=${ODL_HOME:-/opt/opendaylight/current} +ODL_KARAF_CLIENT=${ODL_KARAF_CLIENT:-${ODL_HOME}/bin/client} +INSTALLERDIR=$(dirname $0) + +REPOZIP=${INSTALLERDIR}/${features.boot}-${project.version}.zip + +if [ -f ${REPOZIP} ] +then + unzip -nd ${ODL_HOME} ${REPOZIP} +else + echo "ERROR : repo zip ($REPOZIP) not found" + exit 1 +fi + +${ODL_KARAF_CLIENT} feature:repo-add ${features.repositories} +${ODL_KARAF_CLIENT} feature:install ${features.boot} diff --git a/plugins/template-node/pom.xml b/plugins/template-node/pom.xml new file mode 100644 index 000000000..615c5d348 --- /dev/null +++ b/plugins/template-node/pom.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>pom</packaging> + + <modules> + <module>provider</module> + <module>installer</module> + </modules> +</project> diff --git a/plugins/template-node/provider/pom.xml b/plugins/template-node/provider/pom.xml new file mode 100644 index 000000000..d7bc54fc6 --- /dev/null +++ b/plugins/template-node/provider/pom.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>binding-parent</artifactId> + <version>2.1.0</version> + </parent> + + <groupId>org.onap.ccsdk.sli.plugins</groupId> + <artifactId>template-node-provider</artifactId> + <version>1.1.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>ccsdk-sli-plugins :: template-node :: ${project.artifactId}</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-core-artifacts</artifactId> + <version>${ccsdk.sli.core.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onap.ccsdk.sli.core</groupId> + <artifactId>sli-common</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.velocity</groupId> + <artifactId>velocity</artifactId> + </dependency> + </dependencies> +</project> diff --git a/plugins/template-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/template/TemplateNode.java b/plugins/template-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/template/TemplateNode.java new file mode 100755 index 000000000..005b62067 --- /dev/null +++ b/plugins/template-node/provider/src/main/java/org/onap/ccsdk/sli/plugins/template/TemplateNode.java @@ -0,0 +1,114 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : SDN-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * Modifications Copyright © 2018 IBM. + * ================================================================================ + * 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.ccsdk.sli.plugins.template; + +import java.io.FileInputStream; +import java.io.StringWriter; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; +import org.onap.ccsdk.sli.core.sli.SvcLogicJavaPlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TemplateNode implements SvcLogicJavaPlugin { + private static final Logger logger = LoggerFactory.getLogger(TemplateNode.class); + public static final String TEMPLATE_PATH = "templatePath"; + public static final String OUTPUT_PATH_KEY = "output"; + public static final String PREFIX_KEY = "prefix"; + public static final String REQUIRED_PARAMETERS_ERROR_MESSAGE = "templateName & outputPath are required fields"; + protected static final String TEMPLATE_PROPERTIES_FILE_NAME = "template-node.properties"; + protected static final String DEFAULT_PROPERTIES_DIR = "/opt/onap/ccsdk/data/properties"; + protected static final String PROPERTIES_DIR_KEY = "SDNC_CONFIG_DIR"; + + protected VelocityEngine ve; + + public TemplateNode() { + ve = new VelocityEngine(); + setProperties(); + ve.init(); + } + + protected void setProperties() { + String configDir = System.getProperty(PROPERTIES_DIR_KEY, DEFAULT_PROPERTIES_DIR); + Properties props = new Properties(); + + try (FileInputStream in = new FileInputStream(configDir + "/" + TEMPLATE_PROPERTIES_FILE_NAME)) { + props.load(in); + } catch (Exception e) { + logger.warn("Properties not loaded for template node, using sensible defaults. " + e.getMessage()); + } + + // give sensible defaults for required properties + ve.setProperty(RuntimeConstants.RESOURCE_LOADER, props.getProperty("velocity.resource.loader", "file")); + ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, + props.getProperty("velocity.file.resource.loader.path", "/opt/onap/sdnc/restapi/templates")); + ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, + props.getProperty("velocity.file.resource.loader.cache", "false")); + + // allow flexible reading of additional velocity properties + for (String propertyName : props.stringPropertyNames()) { + if (propertyName.startsWith("velocity")) { + logger.error("set " + propertyName.substring(9) + "=" + props.get(propertyName)); + ve.setProperty(propertyName.substring(9), props.get(propertyName)); + } + } + } + + public void evaluateTemplate(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException { + String templateName = params.get(TEMPLATE_PATH); + String outputPath = params.get(OUTPUT_PATH_KEY); + String prefix = params.get(PREFIX_KEY); + + if (prefix != null && prefix.length() > 0) { + outputPath = prefix + "." + outputPath; + } + + if (templateName == null || outputPath == null) { + throw new SvcLogicException(REQUIRED_PARAMETERS_ERROR_MESSAGE); + } else { + try { + Template template = ve.getTemplate(templateName); + VelocityContext context = new VelocityContext(); + context.put("ctx", ctx); + context.put("params", params); + //Adding these values directly to context makes working with the values cleaner + for (Entry<String, String> entry : params.entrySet()) { + context.put(entry.getKey(), entry.getValue()); + } + StringWriter sw = new StringWriter(); + template.merge(context, sw); + ctx.setAttribute(outputPath, sw.toString()); + } catch (Exception e) { + throw new SvcLogicException(e.getMessage()); + } + } + } + +} diff --git a/plugins/template-node/provider/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/plugins/template-node/provider/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..60409c504 --- /dev/null +++ b/plugins/template-node/provider/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <bean id="templateNode" + class="org.onap.ccsdk.sli.plugins.template.TemplateNode" /> + + <service id="templateNodeService" ref="templateNode" + interface="org.onap.ccsdk.sli.plugins.template.TemplateNode" /> + +</blueprint>
\ No newline at end of file diff --git a/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/MockTemplateNode.java b/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/MockTemplateNode.java new file mode 100644 index 000000000..d628ac929 --- /dev/null +++ b/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/MockTemplateNode.java @@ -0,0 +1,7 @@ +package org.onap.ccsdk.sli.plugins.template; + +public class MockTemplateNode extends TemplateNode { + protected void setProperties() { + // do nothing + } +} diff --git a/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/TemplateNodeTest.java b/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/TemplateNodeTest.java new file mode 100644 index 000000000..29c5c973e --- /dev/null +++ b/plugins/template-node/provider/src/test/java/org/onap/ccsdk/sli/plugins/template/TemplateNodeTest.java @@ -0,0 +1,85 @@ +package org.onap.ccsdk.sli.plugins.template; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import org.apache.velocity.runtime.RuntimeConstants; +import org.junit.Test; +import org.onap.ccsdk.sli.core.sli.SvcLogicContext; +import org.onap.ccsdk.sli.core.sli.SvcLogicException; + +public class TemplateNodeTest { + + @Test + public void sunnyDay() throws Exception { + String requestId = "REQ001"; + String uniqueKey = "UNIQUE_TEST"; + String action = "uPdaTe"; + String serviceType = "VPN"; + + TemplateNode t = new MockTemplateNode(); + + Map<String, String> params = new HashMap<String, String>(); + params.put(TemplateNode.PREFIX_KEY, "output"); + params.put(TemplateNode.OUTPUT_PATH_KEY, "mycontainer"); + params.put(TemplateNode.TEMPLATE_PATH, "src/test/resources/basic.vtl"); + params.put("service-type", serviceType); + SvcLogicContext ctx = new SvcLogicContext(); + ctx.setAttribute("input.svc-request-id", requestId); + ctx.setAttribute("input.unique-key", uniqueKey); + ctx.setAttribute("action", action); + + t.evaluateTemplate(params, ctx); + String result = ctx.getAttribute("output.mycontainer"); + assertNotNull(result); + assertTrue(result.contains(requestId)); + assertTrue(result.contains(uniqueKey)); + assertTrue(result.contains(action.toUpperCase())); + assertTrue(result.contains(serviceType)); + } + + @Test(expected = SvcLogicException.class) + public void parameterException() throws Exception { + TemplateNode t = new MockTemplateNode(); + Map<String, String> params = new HashMap<String, String>(); + SvcLogicContext ctx = new SvcLogicContext(); + t.evaluateTemplate(params, ctx); + } + + @Test(expected = SvcLogicException.class) + public void missingTemplate() throws Exception { + TemplateNode t = new MockTemplateNode(); + Map<String, String> params = new HashMap<String, String>(); + params.put(TemplateNode.PREFIX_KEY, "output"); + params.put(TemplateNode.OUTPUT_PATH_KEY, "mycontainer"); + params.put(TemplateNode.TEMPLATE_PATH, "src/test/resources/missing.vtl"); + SvcLogicContext ctx = new SvcLogicContext(); + t.evaluateTemplate(params, ctx); + } + + @Test + public void withProperties() throws Exception { + System.setProperty(TemplateNode.PROPERTIES_DIR_KEY, "src/test/resources"); + TemplateNode t = new TemplateNode(); + Vector<String> loader = (Vector<String>) t.ve.getProperty(RuntimeConstants.RESOURCE_LOADER); + assertTrue(loader.contains("class")); + assertEquals("/home/my/example", t.ve.getProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH)); + assertEquals("true", t.ve.getProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE)); + assertEquals("customValue", t.ve.getProperty("custom.property")); + } + + @Test + public void withNoProperties() throws Exception { + System.setProperty(TemplateNode.PROPERTIES_DIR_KEY, "i/do/not/exist"); + TemplateNode t = new TemplateNode(); + Vector<String> loader = (Vector<String>) t.ve.getProperty(RuntimeConstants.RESOURCE_LOADER); + assertTrue(loader.contains("file")); + assertEquals("/opt/onap/sdnc/restapi/templates", t.ve.getProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH)); + assertEquals("false", t.ve.getProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE)); + assertEquals(null, t.ve.getProperty("custom.property")); + } + +}
\ No newline at end of file diff --git a/plugins/template-node/provider/src/test/resources/basic.vtl b/plugins/template-node/provider/src/test/resources/basic.vtl new file mode 100644 index 000000000..63fcc670a --- /dev/null +++ b/plugins/template-node/provider/src/test/resources/basic.vtl @@ -0,0 +1,12 @@ +{ + "input": { + "sdnc-request-header": { + "svc-request-id": "$ctx.getAttribute("input.svc-request-id")", + "svc-action": "$ctx.getAttribute("action").toUpperCase()" + }, + "service-information": { + "service-type": "$params.get("service-type")", + "service-instance-id": "$ctx.getAttribute("input.unique-key")" + } + } +}
\ No newline at end of file diff --git a/plugins/template-node/provider/src/test/resources/template-node.properties b/plugins/template-node/provider/src/test/resources/template-node.properties new file mode 100644 index 000000000..1199a0cde --- /dev/null +++ b/plugins/template-node/provider/src/test/resources/template-node.properties @@ -0,0 +1,4 @@ +velocity.resource.loader=class +velocity.file.resource.loader.path=/home/my/example +velocity.file.resource.loader.cache=true +velocity.custom.property=customValue
\ No newline at end of file |