diff options
Diffstat (limited to 'netconf')
437 files changed, 53387 insertions, 0 deletions
diff --git a/netconf/features/pom.xml b/netconf/features/pom.xml new file mode 100644 index 0000000..c326f18 --- /dev/null +++ b/netconf/features/pom.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html +--> +<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.opendaylight.odlparent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>10.0.2</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.odl-legacy</groupId> + <artifactId>netconf-features-parent</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + <maven.install.skip>true</maven.install.skip> + </properties> + + <modules> + <module>restconf</module> + </modules> + +</project> diff --git a/netconf/features/restconf/odl-restconf-nb-bierman02/pom.xml b/netconf/features/restconf/odl-restconf-nb-bierman02/pom.xml new file mode 100644 index 0000000..93ed2d2 --- /dev/null +++ b/netconf/features/restconf/odl-restconf-nb-bierman02/pom.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2019 Pantheon Technologies, s.r.o. and others. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<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.opendaylight.netconf</groupId> + <artifactId>feature-parent</artifactId> + <version>3.0.5</version> + <relativePath>../../parent</relativePath> + </parent> + + <artifactId>odl-restconf-nb-bierman02</artifactId> + <packaging>feature</packaging> + <name>OpenDaylight :: Restconf :: NB :: bierman02</name> + + <dependencies> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>odl-netconf-mapping-api</artifactId> + <type>xml</type> + <classifier>features</classifier> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>odl-restconf-common</artifactId> + <type>xml</type> + <classifier>features</classifier> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-nb-bierman02</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>sal-rest-connector-config</artifactId> + <type>cfg</type> + <classifier>restconf</classifier> + </dependency> + + <!-- TODO: Remove this when RESTCONF is converted to GSON --> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20131018</version> + </dependency> + </dependencies> +</project> diff --git a/netconf/features/restconf/pom.xml b/netconf/features/restconf/pom.xml new file mode 100644 index 0000000..cf460cd --- /dev/null +++ b/netconf/features/restconf/pom.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2017 Red Hat, Inc. and others. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<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.opendaylight.odlparent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>10.0.2</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.odk-legacy</groupId> + <artifactId>features-restconf-aggregator</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + <maven.install.skip>true</maven.install.skip> + </properties> + + <modules> + <module>odl-restconf-nb-bierman02</module> + </modules> +</project> diff --git a/netconf/pom.xml b/netconf/pom.xml new file mode 100644 index 0000000..e7a85d7 --- /dev/null +++ b/netconf/pom.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html +--> +<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.opendaylight.odlparent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>10.0.2</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.odl-legacy</groupId> + <artifactId>netconf-aggregator</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + <name>${project.artifactId}</name> + + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + <maven.install.skip>true</maven.install.skip> + </properties> + + <modules> + <module>features</module> + <module>restconf</module> + </modules> + + <profiles> + <profile> + <id>sonar-jacoco-aggregate</id> + <activation> + <property> + <name>odl.jacoco.aggregateFile</name> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <executions> + <execution> + <id>merge</id> + <goals> + <goal>merge</goal> + </goals> + <phase>generate-resources</phase> + <configuration> + <destFile>${odl.jacoco.aggregateFile}</destFile> + <fileSets> + <fileSet> + <directory>${project.basedir}</directory> + <includes> + <include>**/target/code-coverage/*.exec</include> + </includes> + </fileSet> + </fileSets> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> diff --git a/netconf/restconf/pom.xml b/netconf/restconf/pom.xml new file mode 100644 index 0000000..b85e1b3 --- /dev/null +++ b/netconf/restconf/pom.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html +--> +<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.opendaylight.odlparent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>10.0.2</version> + <relativePath/> + </parent> + + <groupId>org.onap.ccsdk.odl-legacy</groupId> + <artifactId>restconf-subsystem</artifactId> + <version>1.0.0</version> + <packaging>pom</packaging> + <name>${project.artifactId}</name> + + <properties> + <maven.deploy.skip>true</maven.deploy.skip> + <maven.install.skip>true</maven.install.skip> + </properties> + + <modules> + <module>restconf-nb-bierman02</module> + </modules> + +</project> diff --git a/netconf/restconf/restconf-nb-bierman02/pom.xml b/netconf/restconf/restconf-nb-bierman02/pom.xml new file mode 100644 index 0000000..f560d00 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/pom.xml @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html +--> +<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.opendaylight.netconf</groupId> + <artifactId>netconf-parent</artifactId> + <version>3.0.5</version> + </parent> + + <groupId>org.onap.ccsdk.odl-legacy</groupId> + <artifactId>restconf-nb-bierman02</artifactId> + <version>1.0.0</version> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>javax.annotation-api</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>com.guicedee.services</groupId> + <artifactId>javax.inject</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-common-models</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>restconf-common</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.inject</groupId> + <artifactId>jersey-hk2</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal</groupId> + <artifactId>mdsal-dom-api</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal</groupId> + <artifactId>mdsal-dom-spi</artifactId> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-codec-http</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.controller</groupId> + <artifactId>sal-common-util</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-data-api</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-data-impl</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-model-util</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-data-codec-gson</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-data-codec-xml</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-model-export</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId> + <artifactId>rfc6991-ietf-inet-types</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal.binding.model.ietf</groupId> + <artifactId>rfc6991-ietf-yang-types</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.netconf</groupId> + <artifactId>netconf-util</artifactId> + </dependency> + <dependency> + <groupId>net.java.dev.stax-utils</groupId> + <artifactId>stax-utils</artifactId> + <exclusions> + <exclusion> + <!-- JSR173 ships with JRE by default --> + <groupId>com.bea.xml</groupId> + <artifactId>jsr173-ri</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20131018</version> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa.web</groupId> + <artifactId>web-api</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa.web</groupId> + <artifactId>servlet-api</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa</groupId> + <artifactId>aaa-filterchain</artifactId> + </dependency> + + <!-- Testing Dependencies --> + <dependency> + <groupId>org.glassfish.jersey.test-framework.providers</groupId> + <artifactId>jersey-test-framework-provider-grizzly2</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa</groupId> + <artifactId>aaa-shiro-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>testutils</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal</groupId> + <artifactId>mdsal-binding-test-utils</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.mdsal</groupId> + <artifactId>mdsal-binding-dom-adapter</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava-testlib</artifactId> + </dependency> + <dependency> + <groupId>org.skyscreamer</groupId> + <artifactId>jsonassert</artifactId> + </dependency> + <dependency> + <groupId>org.opendaylight.aaa.web</groupId> + <artifactId>testutils</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.infrautils</groupId> + <artifactId>inject.guice.testutils</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.opendaylight.yangtools</groupId> + <artifactId>yang-test-util</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Name>MD SAL Restconf Connector</Bundle-Name> + <Private-Package>org.opendaylight.netconf.sal.rest.*, + org.opendaylight.netconf.sal.restconf.rpc.*, + org.opendaylight.netconf.sal.restconf.impl.*, + org.opendaylight.netconf.md.sal.rest.common.*, + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.rest.connector.rev140724.*, + </Private-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + + <scm> + <connection>scm:git:ssh://git.opendaylight.org:29418/netconf.git</connection> + <developerConnection>scm:git:ssh://git.opendaylight.org:29418/netconf.git</developerConnection> + <tag>HEAD</tag> + <url>https://git.opendaylight.org/gerrit/gitweb?p=netconf.git;a=summary</url> + </scm> +</project> diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java new file mode 100644 index 0000000..c377650 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.md.sal.rest.schema; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.concurrent.ExecutionException; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import org.opendaylight.restconf.common.schema.SchemaExportContext; +import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; + +@Provider +@Produces({ YangConstants.RFC6020_YANG_MEDIA_TYPE }) +public class SchemaExportContentYangBodyWriter implements MessageBodyWriter<SchemaExportContext> { + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(SchemaExportContext.class); + } + + @Override + public long getSize(final SchemaExportContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final SchemaExportContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, + WebApplicationException { + final RevisionSourceIdentifier sourceId = RevisionSourceIdentifier.create(context.getModule().getName(), + context.getModule().getQNameModule().getRevision()); + final YangTextSchemaSource yangTextSchemaSource; + try { + yangTextSchemaSource = context.getSourceProvider().getSource(sourceId).get(); + } catch (InterruptedException | ExecutionException e) { + throw new WebApplicationException("Unable to retrieve source from SourceProvider.", e); + } + yangTextSchemaSource.copyTo(entityStream); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java new file mode 100644 index 0000000..9f4f8be --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.md.sal.rest.schema; + +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.stream.XMLStreamException; +import org.opendaylight.restconf.common.schema.SchemaExportContext; +import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.model.export.YinExportUtils; + +@Provider +@Produces({ YangConstants.RFC6020_YIN_MEDIA_TYPE }) +public class SchemaExportContentYinBodyWriter implements MessageBodyWriter<SchemaExportContext> { + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(SchemaExportContext.class); + } + + @Override + public long getSize(final SchemaExportContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final SchemaExportContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws + WebApplicationException { + try { + YinExportUtils.writeModuleAsYinText(context.getModule(), entityStream); + } catch (final XMLStreamException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalService.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalService.java new file mode 100644 index 0000000..a20d041 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalService.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.md.sal.rest.schema; + +import com.google.common.annotations.Beta; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import org.opendaylight.restconf.common.schema.SchemaExportContext; +import org.opendaylight.yangtools.yang.common.YangConstants; + +/** + * Retrieval of the YANG modules which server supports. + * + * @deprecated do not use this api. It is replaced by RestconfSchemaService + */ +@Deprecated +@Beta +public interface SchemaRetrievalService { + @GET + @Produces({YangConstants.RFC6020_YIN_MEDIA_TYPE, YangConstants.RFC6020_YANG_MEDIA_TYPE}) + @Path("/modules/module/{identifier:.+}/schema") + SchemaExportContext getSchema(@PathParam("identifier") String mountAndModuleId); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalServiceImpl.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalServiceImpl.java new file mode 100644 index 0000000..ad23992 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalServiceImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.md.sal.rest.schema; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import java.time.format.DateTimeParseException; +import java.util.Iterator; +import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.schema.SchemaExportContext; +import org.opendaylight.restconf.common.validation.RestconfValidationUtils; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class SchemaRetrievalServiceImpl implements SchemaRetrievalService { + + private final ControllerContext salContext; + + private static final Splitter SLASH_SPLITTER = Splitter.on("/"); + private static final String MOUNT_ARG = ControllerContext.MOUNT; + + public SchemaRetrievalServiceImpl(final ControllerContext controllerContext) { + salContext = controllerContext; + } + + + @Override + public SchemaExportContext getSchema(final String mountAndModule) { + final SchemaContext schemaContext; + final Iterable<String> pathComponents = SLASH_SPLITTER.split(mountAndModule); + final Iterator<String> componentIter = pathComponents.iterator(); + if (!Iterables.contains(pathComponents, MOUNT_ARG)) { + schemaContext = salContext.getGlobalSchema(); + } else { + final StringBuilder pathBuilder = new StringBuilder(); + while (componentIter.hasNext()) { + final String current = componentIter.next(); + // It is argument, not last element. + if (pathBuilder.length() != 0) { + pathBuilder.append("/"); + } + pathBuilder.append(current); + if (MOUNT_ARG.equals(current)) { + // We stop right at mountpoint, last two arguments should + // be module name and revision + break; + } + } + schemaContext = getMountSchemaContext(pathBuilder.toString()); + + } + + RestconfDocumentedException.throwIf(!componentIter.hasNext(), "Module name must be supplied.", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + final String moduleName = componentIter.next(); + RestconfDocumentedException.throwIf(!componentIter.hasNext(), "Revision date must be supplied.", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + final String revisionString = componentIter.next(); + return getExportUsingNameAndRevision(schemaContext, moduleName, revisionString, + salContext.getYangTextSourceProvider()); + } + + private static SchemaExportContext getExportUsingNameAndRevision(final SchemaContext schemaContext, + final String moduleName, final String revisionStr, + final DOMYangTextSourceProvider yangTextSourceProvider) { + try { + final Module module = schemaContext.findModule(moduleName, Revision.of(revisionStr)).orElse(null); + return new SchemaExportContext( + schemaContext, RestconfValidationUtils.checkNotNullDocumented(module, moduleName), + yangTextSourceProvider); + } catch (final DateTimeParseException e) { + throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", e); + } + } + + private SchemaContext getMountSchemaContext(final String identifier) { + final InstanceIdentifierContext mountContext = salContext.toMountPointIdentifier(identifier); + return mountContext.getSchemaContext(); + } +} + diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/Draft02.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/Draft02.java new file mode 100644 index 0000000..67c8729 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/Draft02.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.api; + +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Base Draft for Restconf project. + * + * @deprecated Do not use old implementation of restconf draft. It will be replaced by Rfc8040. + */ +@Deprecated +public class Draft02 { + public interface MediaTypes { + String API = "application/yang.api"; + String DATASTORE = "application/yang.datastore"; + String DATA = "application/yang.data"; + String OPERATION = "application/yang.operation"; + String PATCH = "application/yang.patch"; + String PATCH_STATUS = "application/yang.patch-status"; + String STREAM = "application/yang.stream"; + } + + public interface RestConfModule { + String REVISION = "2013-10-19"; + + String NAME = "ietf-restconf"; + + String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"; + + String RESTCONF_GROUPING_SCHEMA_NODE = "restconf"; + + String RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"; + + String MODULES_CONTAINER_SCHEMA_NODE = "modules"; + + String MODULE_LIST_SCHEMA_NODE = "module"; + + String STREAMS_CONTAINER_SCHEMA_NODE = "streams"; + + String STREAM_LIST_SCHEMA_NODE = "stream"; + + String ERROR_LIST_SCHEMA_NODE = "error"; + + QName IETF_RESTCONF_QNAME = QName.create(Draft02.RestConfModule.NAMESPACE, Draft02.RestConfModule.REVISION, + Draft02.RestConfModule.NAME); + + QName ERRORS_QNAME = QName.create(IETF_RESTCONF_QNAME, "errors"); + + QName ERROR_LIST_QNAME = QName.create(IETF_RESTCONF_QNAME, ERROR_LIST_SCHEMA_NODE); + + QName ERROR_TYPE_QNAME = QName.create(IETF_RESTCONF_QNAME, "error-type"); + + QName ERROR_TAG_QNAME = QName.create(IETF_RESTCONF_QNAME, "error-tag"); + + QName ERROR_APP_TAG_QNAME = QName.create(IETF_RESTCONF_QNAME, "error-app-tag"); + + QName ERROR_MESSAGE_QNAME = QName.create(IETF_RESTCONF_QNAME, "error-message"); + + QName ERROR_INFO_QNAME = QName.create(IETF_RESTCONF_QNAME, "error-info"); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestConnector.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestConnector.java new file mode 100644 index 0000000..3200958 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestConnector.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.rest.api; + +/* + * This is a simple dummy interface to allow us to create instances of RestconfProvider + * via the config subsystem. + */ +public interface RestConnector { + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfConstants.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfConstants.java new file mode 100644 index 0000000..6a4ba93 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfConstants.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.rest.api; + +public interface RestconfConstants { + + String IDENTIFIER = "identifier"; + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfNormalizedNodeWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfNormalizedNodeWriter.java new file mode 100644 index 0000000..eb160bd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfNormalizedNodeWriter.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.api; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface RestconfNormalizedNodeWriter extends Flushable, Closeable { + + RestconfNormalizedNodeWriter write(NormalizedNode node) throws IOException; +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java new file mode 100644 index 0000000..0d0d2ab --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.api; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.Encoded; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.restconf.common.patch.Patch; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; + +/** + * The URI hierarchy for the RESTCONF resources consists of an entry point + * container, 4 top-level resources, and 1 field. + * <ul> + * <li><b>/restconf</b> - {@link #getRoot()} + * <ul> + * <li><b>/config</b> - {@link #readConfigurationData(String, UriInfo)} + * {@link #updateConfigurationData(String, NormalizedNodeContext, UriInfo)} + * {@link #createConfigurationData(NormalizedNodeContext, UriInfo)} + * {@link #createConfigurationData(String, NormalizedNodeContext, UriInfo)} + * {@link #deleteConfigurationData(String)} + * <li><b>/operational</b> - {@link #readOperationalData(String, UriInfo)} + * <li>/modules - {@link #getModules(UriInfo)} + * <ul> + * <li>/module + * </ul> + * <li><b>/operations</b> - + * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)} + * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)} + * <li>/version (field) + * </ul> + * </ul> + */ +@Path("/") +public interface RestconfService { + + String XML = "+xml"; + String JSON = "+json"; + + @GET + Object getRoot(); + + /** + * Get all modules supported by controller. + * + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(UriInfo) + */ + @Deprecated + @GET + @Path("/modules") + @Produces({ + Draft02.MediaTypes.API + JSON, + Draft02.MediaTypes.API + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext getModules(@Context UriInfo uriInfo); + + /** + * Get all modules supported by mount point. + * + * @param identifier + * mount point identifier + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(String, + * UriInfo) + */ + @Deprecated + @GET + @Path("/modules/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.API + JSON, + Draft02.MediaTypes.API + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); + + /** + * Get module. + * + * @param identifier + * path to target + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(String, + * UriInfo) + */ + @Deprecated + @GET + @Path("/modules/module/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.API + JSON, + Draft02.MediaTypes.API + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); + + /** + * List of rpc or action operations supported by the server. + * + * @return A JSON document string + * @deprecated do not use this method. It will be replaced by + * RestconfOperationsService#getOperations(UriInfo) + */ + @Deprecated + @GET + @Path("/operations") + @Produces({ Draft02.MediaTypes.API + JSON, MediaType.APPLICATION_JSON }) + String getOperationsJSON(); + + /** + * List of rpc or action operations supported by the server. + * + * @return A XML document string + * @deprecated do not use this method. It will be replaced by + * RestconfOperationsService#getOperations(UriInfo) + */ + @Deprecated + @GET + @Path("/operations") + @Produces({ Draft02.MediaTypes.API + XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + String getOperationsXML(); + + /** + * Valid for mount points. List of operations supported by the server. + * + * @param identifier + * path parameter + * @param uriInfo + * URI information + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by + * RestconfOperationsService#getOperations(String, UriInfo) + */ + @Deprecated + @GET + @Path("/operations/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.API + JSON, + Draft02.MediaTypes.API + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext getOperations(@PathParam("identifier") String identifier, @Context UriInfo uriInfo); + + /** + * Invoke RPC operation. + * + * @param identifier + * module name and rpc identifier string for the desired operation + * @param payload + * {@link NormalizedNodeContext} - the body of the operation + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by + * RestconfInvokeOperationsService#invokeRpc(String, NormalizedNodeContext, UriInfo) + */ + @Deprecated + @POST + @Path("/operations/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.OPERATION + JSON, + Draft02.MediaTypes.OPERATION + XML, + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + @Consumes({ + Draft02.MediaTypes.OPERATION + JSON, + Draft02.MediaTypes.OPERATION + XML, + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext invokeRpc(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload, + @Context UriInfo uriInfo); + + /** + * Get target data resource from config data store. + * + * @param identifier + * path to target + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(String, + * UriInfo) + */ + @Deprecated + @GET + @Path("/config/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext readConfigurationData(@Encoded @PathParam("identifier") String identifier, + @Context UriInfo uriInfo); + + /** + * Get target data resource from operational data store. + * + * @param identifier + * path to target + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(String, + * UriInfo) + */ + @Deprecated + @GET + @Path("/operational/{identifier:.+}") + @Produces({ + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext readOperationalData(@Encoded @PathParam("identifier") String identifier, + @Context UriInfo uriInfo); + + /** + * Create or replace the target data resource. + * + * @param identifier + * path to target + * @param payload + * data node for put to config DS + * @return {@link Response} + * @deprecated do not use this method. It will be replaced by RestconfDataService#putData(String, + * NormalizedNodeContext, UriInfo) + */ + @Deprecated + @PUT + @Path("/config/{identifier:.+}") + @Consumes({ + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + Response updateConfigurationData(@Encoded @PathParam("identifier") String identifier, + NormalizedNodeContext payload, @Context UriInfo uriInfo); + + /** + * Create a data resource in target. + * + * @param identifier + * path to target + * @param payload + * new data + * @param uriInfo + * URI info + * @return {@link Response} + * @deprecated do not use this method. It will be replaced by RestconfDataService#postData(String, + * NormalizedNodeContext, UriInfo) + */ + @Deprecated + @POST + @Path("/config/{identifier:.+}") + @Consumes({ + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + Response createConfigurationData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload, + @Context UriInfo uriInfo); + + /** + * Create a data resource. + * + * @param payload + * new data + * @param uriInfo + * URI info + * @return {@link Response} + * @deprecated do not use this method. It will be replaced by + * RestconfDataService#postData(NormalizedNodeContext, UriInfo) + */ + @Deprecated + @POST + @Path("/config") + @Consumes({ + Draft02.MediaTypes.DATA + JSON, + Draft02.MediaTypes.DATA + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + Response createConfigurationData(NormalizedNodeContext payload, @Context UriInfo uriInfo); + + /** + * Delete the target data resource. + * + * @param identifier + * path to target + * @return {@link Response} + * @deprecated do not use this method. It will be replaced by RestconfDataService#deleteData(String) + */ + @Deprecated + @DELETE + @Path("/config/{identifier:.+}") + Response deleteConfigurationData(@Encoded @PathParam("identifier") String identifier); + + /** + * Subscribe to stream. + * + * @param identifier + * stream identifier + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by + * RestconfStreamsSubscriptionService#subscribeToStream(String, UriInfo) + */ + @Deprecated + @GET + @Path("/streams/stream/{identifier:.+}") + NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier, + @Context UriInfo uriInfo); + + /** + * Get list of all streams. + * + * @param uriInfo + * URI info + * @return {@link NormalizedNodeContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#readData(String, + * UriInfo) + **/ + @Deprecated + @GET + @Path("/streams") + @Produces({ + Draft02.MediaTypes.API + JSON, + Draft02.MediaTypes.API + XML, + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML + }) + NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo); + + /** + * Ordered list of edits that are applied to the target datastore by the server. + * + * @param identifier + * path to target + * @param context + * edits + * @param uriInfo + * URI info + * @return {@link PatchStatusContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#patchData(String, + * PatchContext, UriInfo) + */ + @Deprecated + @Patch + @Path("/config/{identifier:.+}") + @Consumes({ + MediaTypes.PATCH + JSON, + MediaTypes.PATCH + XML + }) + @Produces({ + MediaTypes.PATCH_STATUS + JSON, + MediaTypes.PATCH_STATUS + XML + }) + PatchStatusContext patchConfigurationData(@Encoded @PathParam("identifier") String identifier, PatchContext + context, @Context UriInfo uriInfo); + + /** + * Ordered list of edits that are applied to the datastore by the server. + * + * @param context + * edits + * @param uriInfo + * URI info + * @return {@link PatchStatusContext} + * @deprecated do not use this method. It will be replaced by RestconfDataService#patchData(PatchContext, + * UriInfo) + */ + @Deprecated + @Patch + @Path("/config") + @Consumes({ + MediaTypes.PATCH + JSON, + MediaTypes.PATCH + XML + }) + @Produces({ + MediaTypes.PATCH_STATUS + JSON, + MediaTypes.PATCH_STATUS + XML + }) + PatchStatusContext patchConfigurationData(PatchContext context, @Context UriInfo uriInfo); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/package-info.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/package-info.java new file mode 100644 index 0000000..b765331 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/package-info.java @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.api; + diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java new file mode 100644 index 0000000..76ffdb4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/AbstractIdentifierAwareJaxRsProvider.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.rest.impl; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.netconf.sal.rest.api.RestconfConstants; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; + +/** + * JAX-RS Provider. + * + * @deprecated This class will be replaced by AbstractIdentifierAwareJaxRsProvider in restconf-nb-rfc8040 + */ +@Deprecated +public class AbstractIdentifierAwareJaxRsProvider { + + private static final String POST = "POST"; + + @Context + private UriInfo uriInfo; + + @Context + private Request request; + + private final ControllerContext controllerContext; + + protected AbstractIdentifierAwareJaxRsProvider(final ControllerContext controllerContext) { + this.controllerContext = controllerContext; + } + + protected final String getIdentifier() { + return uriInfo.getPathParameters(false).getFirst(RestconfConstants.IDENTIFIER); + } + + protected InstanceIdentifierContext getInstanceIdentifierContext() { + return controllerContext.toInstanceIdentifier(getIdentifier()); + } + + protected UriInfo getUriInfo() { + return uriInfo; + } + + protected boolean isPost() { + return POST.equals(request.getMethod()); + } + + protected ControllerContext getControllerContext() { + return controllerContext; + } + + Request getRequest() { + return request; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriter.java new file mode 100644 index 0000000..b9d7a7a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriter.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static java.util.Objects.requireNonNull; +import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; + +import com.google.common.collect.Iterables; +import java.io.IOException; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is an experimental iterator over a {@link NormalizedNode}. This is essentially the opposite of a + * {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over the backing data, this + * encapsulates a {@link NormalizedNodeStreamWriter} and allows us to write multiple nodes. + * + * @deprecated This class will be replaced by ParameterAwareNormalizedNodeWriter in restconf-nb-rfc8040 + */ +@Deprecated +public class DepthAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter { + private final NormalizedNodeStreamWriter writer; + protected int currentDepth = 0; + protected final int maxDepth; + + private DepthAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final int maxDepth) { + this.writer = requireNonNull(writer); + this.maxDepth = maxDepth; + } + + protected final NormalizedNodeStreamWriter getWriter() { + return writer; + } + + /** + * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. + * + * @param writer Back-end writer + * @return A new instance. + */ + public static DepthAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, + final int maxDepth) { + return forStreamWriter(writer, true, maxDepth); + } + + /** + * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. + * Unlike the simple {@link #forStreamWriter(NormalizedNodeStreamWriter, int)} + * method, this allows the caller to switch off RFC6020 XML compliance, providing better + * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding + * to emit leaf nodes which participate in a list's key first and in the order in which + * they are defined in the key. For JSON, this requirement is completely relaxed and leaves + * can be ordered in any way we see fit. The former requires a bit of work: first a lookup + * for each key and then for each emitted node we need to check whether it was already + * emitted. + * + * @param writer Back-end writer + * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant. + * @return A new instance. + */ + public static DepthAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, + final boolean orderKeyLeaves, final int maxDepth) { + return orderKeyLeaves ? new OrderedDepthAwareNormalizedNodeWriter(writer, maxDepth) + : new DepthAwareNormalizedNodeWriter(writer, maxDepth); + } + + /** + * Iterate over the provided {@link NormalizedNode} and emit write + * events to the encapsulated {@link NormalizedNodeStreamWriter}. + * + * @param node Node + * @return DepthAwareNormalizedNodeWriter + * @throws IOException when thrown from the backing writer. + */ + @Override + public final DepthAwareNormalizedNodeWriter write(final NormalizedNode node) throws IOException { + if (wasProcessedAsCompositeNode(node)) { + return this; + } + + if (wasProcessAsSimpleNode(node)) { + return this; + } + + throw new IllegalStateException("It wasn't possible to serialize node " + node); + } + + @Override + public void flush() throws IOException { + writer.flush(); + } + + @Override + public void close() throws IOException { + writer.flush(); + writer.close(); + } + + /** + * Emit a best guess of a hint for a particular set of children. It evaluates the + * iterable to see if the size can be easily gotten to. If it is, we hint at the + * real number of child nodes. Otherwise we emit UNKNOWN_SIZE. + * + * @param children Child nodes + * @return Best estimate of the collection size required to hold all the children. + */ + static final int childSizeHint(final Iterable<?> children) { + return children instanceof Collection ? ((Collection<?>) children).size() : UNKNOWN_SIZE; + } + + private boolean wasProcessAsSimpleNode(final NormalizedNode node) throws IOException { + if (node instanceof LeafSetEntryNode) { + if (currentDepth < maxDepth) { + final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>) node; + writer.startLeafSetEntryNode(nodeAsLeafList.getIdentifier()); + writer.scalarValue(nodeAsLeafList.body()); + writer.endNode(); + } + return true; + } else if (node instanceof LeafNode) { + final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node; + writer.startLeafNode(nodeAsLeaf.getIdentifier()); + writer.scalarValue(nodeAsLeaf.body()); + writer.endNode(); + return true; + } else if (node instanceof AnyxmlNode) { + final AnyxmlNode<?> anyxmlNode = (AnyxmlNode<?>)node; + final Class<?> objectModel = anyxmlNode.bodyObjectModel(); + if (writer.startAnyxmlNode(anyxmlNode.getIdentifier(), objectModel)) { + if (DOMSource.class.isAssignableFrom(objectModel)) { + writer.domSourceValue((DOMSource) anyxmlNode.body()); + } else { + writer.scalarValue(anyxmlNode.body()); + } + writer.endNode(); + } + return true; + } + + return false; + } + + /** + * Emit events for all children and then emit an endNode() event. + * + * @param children Child iterable + * @return True + * @throws IOException when the writer reports it + */ + protected final boolean writeChildren(final Iterable<? extends NormalizedNode> children) throws IOException { + if (currentDepth < maxDepth) { + for (final NormalizedNode child : children) { + write(child); + } + } + writer.endNode(); + return true; + } + + protected boolean writeMapEntryChildren(final MapEntryNode mapEntryNode) throws IOException { + if (currentDepth < maxDepth) { + writeChildren(mapEntryNode.body()); + } else if (currentDepth == maxDepth) { + writeOnlyKeys(mapEntryNode.getIdentifier().entrySet()); + } + return true; + } + + private void writeOnlyKeys(final Set<Entry<QName, Object>> entries) throws IOException { + for (final Entry<QName, Object> entry : entries) { + writer.startLeafNode(new NodeIdentifier(entry.getKey())); + writer.scalarValue(entry.getValue()); + writer.endNode(); + } + writer.endNode(); + } + + protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException { + writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.body())); + currentDepth++; + writeMapEntryChildren(node); + currentDepth--; + return true; + } + + private boolean wasProcessedAsCompositeNode(final NormalizedNode node) throws IOException { + boolean processedAsCompositeNode = false; + if (node instanceof ContainerNode) { + final ContainerNode n = (ContainerNode) node; + writer.startContainerNode(n.getIdentifier(), childSizeHint(n.body())); + currentDepth++; + processedAsCompositeNode = writeChildren(n.body()); + currentDepth--; + } else if (node instanceof MapEntryNode) { + processedAsCompositeNode = writeMapEntryNode((MapEntryNode) node); + } else if (node instanceof UnkeyedListEntryNode) { + final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node; + writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.body())); + currentDepth++; + processedAsCompositeNode = writeChildren(n.body()); + currentDepth--; + } else if (node instanceof ChoiceNode) { + final ChoiceNode n = (ChoiceNode) node; + writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.body())); + processedAsCompositeNode = writeChildren(n.body()); + } else if (node instanceof AugmentationNode) { + final AugmentationNode n = (AugmentationNode) node; + writer.startAugmentationNode(n.getIdentifier()); + processedAsCompositeNode = writeChildren(n.body()); + } else if (node instanceof UnkeyedListNode) { + final UnkeyedListNode n = (UnkeyedListNode) node; + writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.body())); + processedAsCompositeNode = writeChildren(n.body()); + } else if (node instanceof UserMapNode) { + final UserMapNode n = (UserMapNode) node; + writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.body())); + processedAsCompositeNode = writeChildren(n.body()); + } else if (node instanceof MapNode) { + final MapNode n = (MapNode) node; + writer.startMapNode(n.getIdentifier(), childSizeHint(n.body())); + processedAsCompositeNode = writeChildren(n.body()); + } else if (node instanceof LeafSetNode) { + final LeafSetNode<?> n = (LeafSetNode<?>) node; + if (node instanceof UserLeafSetNode) { + writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.body())); + } else { + writer.startLeafSet(n.getIdentifier(), childSizeHint(n.body())); + } + currentDepth++; + processedAsCompositeNode = writeChildren(n.body()); + currentDepth--; + } + + return processedAsCompositeNode; + } + + private static final class OrderedDepthAwareNormalizedNodeWriter extends DepthAwareNormalizedNodeWriter { + private static final Logger LOG = LoggerFactory.getLogger(OrderedDepthAwareNormalizedNodeWriter.class); + + OrderedDepthAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final int maxDepth) { + super(writer, maxDepth); + } + + @Override + protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException { + final NormalizedNodeStreamWriter writer = getWriter(); + writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.body())); + + final Set<QName> qnames = node.getIdentifier().keySet(); + // Write out all the key children + for (final QName qname : qnames) { + final Optional<? extends NormalizedNode> child = node.findChildByArg(new NodeIdentifier(qname)); + if (child.isPresent()) { + write(child.get()); + } else { + LOG.info("No child for key element {} found", qname); + } + } + + // Write all the rest + currentDepth++; + final boolean result = writeChildren(Iterables.filter(node.body(), input -> { + if (input instanceof AugmentationNode) { + return true; + } + if (!qnames.contains(input.getIdentifier().getNodeType())) { + return true; + } + + LOG.debug("Skipping key child {}", input); + return false; + })); + currentDepth--; + return result; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java new file mode 100644 index 0000000..aecea7a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.gson.stream.JsonReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Provider +@Consumes({ + Draft02.MediaTypes.DATA + RestconfService.JSON, + Draft02.MediaTypes.OPERATION + RestconfService.JSON, + MediaType.APPLICATION_JSON +}) +public class JsonNormalizedNodeBodyReader + extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> { + + private static final Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class); + + public JsonNormalizedNodeBodyReader(final ControllerContext controllerContext) { + super(controllerContext); + } + + @Override + public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return true; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public NormalizedNodeContext readFrom(final Class<NormalizedNodeContext> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws + WebApplicationException { + try { + return readFrom(getInstanceIdentifierContext(), entityStream, isPost()); + } catch (final Exception e) { + propagateExceptionAs(e); + return null; // no-op + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + public static NormalizedNodeContext readFrom(final String uriPath, final InputStream entityStream, + final boolean isPost, final ControllerContext controllerContext) throws RestconfDocumentedException { + + try { + return readFrom(controllerContext.toInstanceIdentifier(uriPath), entityStream, isPost); + } catch (final Exception e) { + propagateExceptionAs(e); + return null; // no-op + } + } + + private static NormalizedNodeContext readFrom(final InstanceIdentifierContext path, + final InputStream entityStream, final boolean isPost) + throws IOException { + final Optional<InputStream> nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (nonEmptyInputStreamOptional.isEmpty()) { + return new NormalizedNodeContext(path, null); + } + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + + final Inference parentInference; + if (isPost && !(path.getSchemaNode() instanceof RpcDefinition)) { + parentInference = path.inference(); + } else { + final var inference = path.inference(); + if (!inference.statementPath().isEmpty()) { + final var stack = inference.toSchemaInferenceStack(); + stack.exit(); + parentInference = stack.toInference(); + } else { + parentInference = inference; + } + } + + final JsonParserStream jsonParser = JsonParserStream.create(writer, + JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(path.getSchemaContext()), + parentInference); + final JsonReader reader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get(), + StandardCharsets.UTF_8)); + jsonParser.parse(reader); + + NormalizedNode result = resultHolder.getResult(); + final List<YangInstanceIdentifier.PathArgument> iiToDataList = new ArrayList<>(); + InstanceIdentifierContext newIIContext; + + while (result instanceof AugmentationNode || result instanceof ChoiceNode) { + final Object childNode = ((DataContainerNode) result).body().iterator().next(); + if (isPost) { + iiToDataList.add(result.getIdentifier()); + } + result = (NormalizedNode) childNode; + } + + if (isPost) { + if (result instanceof MapEntryNode) { + iiToDataList.add(new YangInstanceIdentifier.NodeIdentifier(result.getIdentifier().getNodeType())); + iiToDataList.add(result.getIdentifier()); + } else { + iiToDataList.add(result.getIdentifier()); + } + } else { + if (result instanceof MapNode) { + result = Iterables.getOnlyElement(((MapNode) result).body()); + } + } + + return new NormalizedNodeContext(path.withConcatenatedArgs(iiToDataList), result); + } + + private static void propagateExceptionAs(final Exception exception) throws RestconfDocumentedException { + Throwables.throwIfInstanceOf(exception, RestconfDocumentedException.class); + LOG.debug("Error parsing json input", exception); + + if (exception instanceof ResultAlreadySetException) { + throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. " + + "Are you creating multiple resources/subresources in POST request?", exception); + } + + RestconfDocumentedException.throwIfYangError(exception); + throw new RestconfDocumentedException("Error parsing input: " + exception.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE, exception); + } +} + diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java new file mode 100644 index 0000000..ce5a8c4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static com.google.common.base.Verify.verify; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchEditOperation; +import org.opendaylight.restconf.common.patch.PatchEntity; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.data.impl.schema.ResultAlreadySetException; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Patch reader for JSON. + * + * @deprecated This class will be replaced by JsonToPatchBodyReader in restconf-nb-rfc8040 + */ +@Deprecated +@Provider +@Consumes({Draft02.MediaTypes.PATCH + RestconfService.JSON}) +public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider + implements MessageBodyReader<PatchContext> { + + private static final Logger LOG = LoggerFactory.getLogger(JsonToPatchBodyReader.class); + + public JsonToPatchBodyReader(final ControllerContext controllerContext) { + super(controllerContext); + } + + @Override + public boolean isReadable(final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return true; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public PatchContext readFrom(final Class<PatchContext> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) + throws WebApplicationException { + try { + return readFrom(getInstanceIdentifierContext(), entityStream); + } catch (final Exception e) { + throw propagateExceptionAs(e); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + public PatchContext readFrom(final String uriPath, final InputStream entityStream) throws + RestconfDocumentedException { + try { + return readFrom(getControllerContext().toInstanceIdentifier(uriPath), entityStream); + } catch (final Exception e) { + propagateExceptionAs(e); + return null; // no-op + } + } + + private PatchContext readFrom(final InstanceIdentifierContext path, final InputStream entityStream) + throws IOException { + final Optional<InputStream> nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (nonEmptyInputStreamOptional.isEmpty()) { + return new PatchContext(path, null, null); + } + + final JsonReader jsonReader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get(), + StandardCharsets.UTF_8)); + AtomicReference<String> patchId = new AtomicReference<>(); + final List<PatchEntity> resultList = read(jsonReader, path, patchId); + jsonReader.close(); + + return new PatchContext(path, resultList, patchId.get()); + } + + private static RuntimeException propagateExceptionAs(final Exception exception) throws RestconfDocumentedException { + Throwables.throwIfInstanceOf(exception, RestconfDocumentedException.class); + LOG.debug("Error parsing json input", exception); + + if (exception instanceof ResultAlreadySetException) { + throw new RestconfDocumentedException("Error parsing json input: Failed to create new parse result data. "); + } + + RestconfDocumentedException.throwIfYangError(exception); + throw new RestconfDocumentedException("Error parsing json input: " + exception.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE, exception); + } + + private List<PatchEntity> read(final JsonReader in, final InstanceIdentifierContext path, + final AtomicReference<String> patchId) throws IOException { + final List<PatchEntity> resultCollection = new ArrayList<>(); + final StringModuleInstanceIdentifierCodec codec = new StringModuleInstanceIdentifierCodec( + path.getSchemaContext()); + final JsonToPatchBodyReader.PatchEdit edit = new JsonToPatchBodyReader.PatchEdit(); + + while (in.hasNext()) { + switch (in.peek()) { + case STRING: + case NUMBER: + in.nextString(); + break; + case BOOLEAN: + Boolean.toString(in.nextBoolean()); + break; + case NULL: + in.nextNull(); + break; + case BEGIN_ARRAY: + in.beginArray(); + break; + case BEGIN_OBJECT: + in.beginObject(); + break; + case END_DOCUMENT: + break; + case NAME: + parseByName(in.nextName(), edit, in, path, codec, resultCollection, patchId); + break; + case END_OBJECT: + in.endObject(); + break; + case END_ARRAY: + in.endArray(); + break; + + default: + break; + } + } + + return ImmutableList.copyOf(resultCollection); + } + + /** + * Switch value of parsed JsonToken.NAME and read edit definition or patch id. + * + * @param name value of token + * @param edit PatchEdit instance + * @param in JsonReader reader + * @param path InstanceIdentifierContext context + * @param codec StringModuleInstanceIdentifierCodec codec + * @param resultCollection collection of parsed edits + * @throws IOException if operation fails + */ + private void parseByName(final @NonNull String name, final @NonNull PatchEdit edit, + final @NonNull JsonReader in, final @NonNull InstanceIdentifierContext path, + final @NonNull StringModuleInstanceIdentifierCodec codec, + final @NonNull List<PatchEntity> resultCollection, + final @NonNull AtomicReference<String> patchId) throws IOException { + switch (name) { + case "edit" : + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + + while (in.hasNext()) { + readEditDefinition(edit, in, path, codec); + resultCollection.add(prepareEditOperation(edit)); + edit.clear(); + } + + in.endArray(); + } else { + readEditDefinition(edit, in, path, codec); + resultCollection.add(prepareEditOperation(edit)); + edit.clear(); + } + + break; + case "patch-id" : + patchId.set(in.nextString()); + break; + default: + break; + } + } + + /** + * Read one patch edit object from Json input. + * @param edit PatchEdit instance to be filled with read data + * @param in JsonReader reader + * @param path InstanceIdentifierContext path context + * @param codec StringModuleInstanceIdentifierCodec codec + * @throws IOException if operation fails + */ + private void readEditDefinition(final @NonNull PatchEdit edit, final @NonNull JsonReader in, + final @NonNull InstanceIdentifierContext path, + final @NonNull StringModuleInstanceIdentifierCodec codec) throws IOException { + final StringBuilder value = new StringBuilder(); + in.beginObject(); + + while (in.hasNext()) { + final String editDefinition = in.nextName(); + switch (editDefinition) { + case "edit-id" : + edit.setId(in.nextString()); + break; + case "operation" : + edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase(Locale.ROOT))); + break; + case "target" : + // target can be specified completely in request URI + final String target = in.nextString(); + if (target.equals("/")) { + edit.setTarget(path.getInstanceIdentifier()); + edit.setTargetSchemaNode(SchemaInferenceStack.of(path.getSchemaContext()).toInference()); + } else { + edit.setTarget(codec.deserialize(codec.serialize(path.getInstanceIdentifier()).concat(target))); + + final var stack = codec.getDataContextTree().enterPath(edit.getTarget()).orElseThrow().stack(); + stack.exit(); + final EffectiveStatement<?, ?> parentStmt = stack.currentStatement(); + verify(parentStmt instanceof SchemaNode, "Unexpected parent %s", parentStmt); + edit.setTargetSchemaNode(stack.toInference()); + } + + break; + case "value" : + // save data defined in value node for next (later) processing, because target needs to be read + // always first and there is no ordering in Json input + readValueNode(value, in); + break; + default: + break; + } + } + + in.endObject(); + + // read saved data to normalized node when target schema is already known + edit.setData(readEditData(new JsonReader( + new StringReader(value.toString())), edit.getTargetSchemaNode(), path)); + } + + /** + * Parse data defined in value node and saves it to buffer. + * @param value Buffer to read value node + * @param in JsonReader reader + * @throws IOException if operation fails + */ + private void readValueNode(final @NonNull StringBuilder value, final @NonNull JsonReader in) throws IOException { + in.beginObject(); + value.append('{'); + + value.append('"').append(in.nextName()).append("\":"); + + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + value.append('['); + + while (in.hasNext()) { + if (in.peek() == JsonToken.STRING) { + value.append('"').append(in.nextString()).append('"'); + } else { + readValueObject(value, in); + } + if (in.peek() != JsonToken.END_ARRAY) { + value.append(','); + } + } + + in.endArray(); + value.append(']'); + } else { + readValueObject(value, in); + } + + in.endObject(); + value.append('}'); + } + + /** + * Parse one value object of data and saves it to buffer. + * @param value Buffer to read value object + * @param in JsonReader reader + * @throws IOException if operation fails + */ + private void readValueObject(final @NonNull StringBuilder value, final @NonNull JsonReader in) throws IOException { + // read simple leaf value + if (in.peek() == JsonToken.STRING) { + value.append('"').append(in.nextString()).append('"'); + return; + } + + in.beginObject(); + value.append('{'); + + while (in.hasNext()) { + value.append('"').append(in.nextName()).append("\":"); + + if (in.peek() == JsonToken.STRING) { + value.append('"').append(in.nextString()).append('"'); + } else { + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + value.append('['); + + while (in.hasNext()) { + if (in.peek() == JsonToken.STRING) { + value.append('"').append(in.nextString()).append('"'); + } else { + readValueObject(value, in); + } + if (in.peek() != JsonToken.END_ARRAY) { + value.append(','); + } + } + + in.endArray(); + value.append(']'); + } else { + readValueObject(value, in); + } + } + + if (in.peek() != JsonToken.END_OBJECT) { + value.append(','); + } + } + + in.endObject(); + value.append('}'); + } + + /** + * Read patch edit data defined in value node to NormalizedNode. + * @param in reader JsonReader reader + * @return NormalizedNode representing data + */ + private static NormalizedNode readEditData(final @NonNull JsonReader in, + final @NonNull Inference targetSchemaNode, final @NonNull InstanceIdentifierContext path) { + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + final EffectiveModelContext context = path.getSchemaContext(); + JsonParserStream.create(writer, JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context), + targetSchemaNode) + .parse(in); + + return resultHolder.getResult(); + } + + /** + * Prepare PatchEntity from PatchEdit instance when it satisfies conditions, otherwise throws exception. + * @param edit Instance of PatchEdit + * @return PatchEntity Patch entity + */ + private static PatchEntity prepareEditOperation(final @NonNull PatchEdit edit) { + if (edit.getOperation() != null && edit.getTargetSchemaNode() != null + && checkDataPresence(edit.getOperation(), edit.getData() != null)) { + if (edit.getOperation().isWithValue()) { + // for lists allow to manipulate with list items through their parent + final YangInstanceIdentifier targetNode; + if (edit.getTarget().getLastPathArgument() instanceof NodeIdentifierWithPredicates) { + targetNode = edit.getTarget().getParent(); + } else { + targetNode = edit.getTarget(); + } + + return new PatchEntity(edit.getId(), edit.getOperation(), targetNode, edit.getData()); + } + + return new PatchEntity(edit.getId(), edit.getOperation(), edit.getTarget()); + } + + throw new RestconfDocumentedException("Error parsing input", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + /** + * Check if data is present when operation requires it and not present when operation data is not allowed. + * @param operation Name of operation + * @param hasData Data in edit are present/not present + * @return true if data is present when operation requires it or if there are no data when operation does not + * allow it, false otherwise + */ + private static boolean checkDataPresence(final @NonNull PatchEditOperation operation, final boolean hasData) { + return operation.isWithValue() == hasData; + } + + /** + * Helper class representing one patch edit. + */ + private static final class PatchEdit { + private String id; + private PatchEditOperation operation; + private YangInstanceIdentifier target; + private Inference targetSchemaNode; + private NormalizedNode data; + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public PatchEditOperation getOperation() { + return operation; + } + + public void setOperation(final PatchEditOperation operation) { + this.operation = operation; + } + + public YangInstanceIdentifier getTarget() { + return target; + } + + public void setTarget(final YangInstanceIdentifier target) { + this.target = target; + } + + public Inference getTargetSchemaNode() { + return targetSchemaNode; + } + + public void setTargetSchemaNode(final Inference targetSchemaNode) { + this.targetSchemaNode = targetSchemaNode; + } + + public NormalizedNode getData() { + return data; + } + + public void setData(final NormalizedNode data) { + this.data = data; + } + + public void clear() { + id = null; + operation = null; + target = null; + targetSchemaNode = null; + data = null; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeContext.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeContext.java new file mode 100644 index 0000000..d52de7d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeContext.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableMap; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +@Deprecated(forRemoval = true, since = "2.0.6") +// Non-final for mocking +public class NormalizedNodeContext { + private final InstanceIdentifierContext context; + private final ImmutableMap<String, Object> headers; + private final WriterParameters writerParameters; + private final NormalizedNode data; + + public NormalizedNodeContext(final InstanceIdentifierContext context, + final NormalizedNode data, final WriterParameters writerParameters, + final ImmutableMap<String, Object> headers) { + this.context = context; + this.data = data; + this.writerParameters = writerParameters; + this.headers = requireNonNull(headers); + } + + public NormalizedNodeContext(final InstanceIdentifierContext context, + final NormalizedNode data, final WriterParameters writerParameters) { + this(context, data, writerParameters, ImmutableMap.of()); + } + + public NormalizedNodeContext(final InstanceIdentifierContext context, + final NormalizedNode data) { + this(context, data, WriterParameters.EMPTY, ImmutableMap.of()); + } + + public NormalizedNodeContext(final InstanceIdentifierContext context, + final NormalizedNode data, final ImmutableMap<String, Object> headers) { + this(context, data, WriterParameters.EMPTY, headers); + } + + public InstanceIdentifierContext getInstanceIdentifierContext() { + return context; + } + + public NormalizedNode getData() { + return data; + } + + public WriterParameters getWriterParameters() { + return writerParameters; + } + + /** + * Return headers of {@code NormalizedNodeContext}. + * + * @return map of headers + */ + public ImmutableMap<String, Object> getNewHeaders() { + return headers; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeJsonBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeJsonBodyWriter.java new file mode 100644 index 0000000..f754df8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeJsonBodyWriter.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Map.Entry; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.stream.XMLStreamException; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.util.NetconfUtil; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.xml.sax.SAXException; + +/** + * Normalized node writer for JSON. + * + * @deprecated This class will be replaced by NormalizedNodeJsonBodyWriter from restconf-nb-rfc8040 + */ +@Deprecated +@Provider +@Produces({ + Draft02.MediaTypes.API + RestconfService.JSON, + Draft02.MediaTypes.DATA + RestconfService.JSON, + Draft02.MediaTypes.OPERATION + RestconfService.JSON, + MediaType.APPLICATION_JSON +}) +public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> { + + private static final int DEFAULT_INDENT_SPACES_NUM = 2; + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(NormalizedNodeContext.class); + } + + @Override + public long getSize(final NormalizedNodeContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final NormalizedNodeContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, + final OutputStream entityStream) throws IOException, WebApplicationException { + if (httpHeaders != null) { + for (final Entry<String, Object> entry : context.getNewHeaders().entrySet()) { + httpHeaders.add(entry.getKey(), entry.getValue()); + } + } + NormalizedNode data = context.getData(); + if (data == null) { + return; + } + + final InstanceIdentifierContext identifierCtx = context.getInstanceIdentifierContext(); + + try (JsonWriter jsonWriter = createJsonWriter(entityStream, context.getWriterParameters().isPrettyPrint())) { + jsonWriter.beginObject(); + writeNormalizedNode(jsonWriter, identifierCtx, data, context.getWriterParameters().getDepth()); + jsonWriter.endObject(); + jsonWriter.flush(); + } + } + + private static void writeNormalizedNode(final JsonWriter jsonWriter, final InstanceIdentifierContext context, + // Note: mutable argument + NormalizedNode data, final @Nullable Integer depth) throws IOException { + + final var stack = context.inference().toSchemaInferenceStack(); + final RestconfNormalizedNodeWriter nnWriter; + if (stack.isEmpty()) { + /* + * Creates writer without initialNs and we write children of root data container + * which is not visible in restconf + */ + nnWriter = createNormalizedNodeWriter(context, context.inference(), jsonWriter, depth); + if (data instanceof ContainerNode) { + writeChildren(nnWriter,(ContainerNode) data); + } else if (data instanceof DOMSourceAnyxmlNode) { + try { + writeChildren(nnWriter, + (ContainerNode) NetconfUtil.transformDOMSourceToNormalizedNode( + context.getSchemaContext(), ((DOMSourceAnyxmlNode)data).body()).getResult()); + } catch (XMLStreamException | URISyntaxException | SAXException e) { + throw new IOException("Cannot write anyxml.", e); + } + } + } else if (context.getSchemaNode() instanceof RpcDefinition) { + /* + * RpcDefinition is not supported as initial codec in JSONStreamWriter, + * so we need to emit initial output declaratation.. + */ + final var rpc = (RpcDefinition) context.getSchemaNode(); + final var tmp = SchemaInferenceStack.of(context.getSchemaContext()); + tmp.enterSchemaTree(rpc.getQName()); + tmp.enterSchemaTree(rpc.getOutput().getQName()); + + nnWriter = createNormalizedNodeWriter(context, tmp.toInference(), jsonWriter, depth); + jsonWriter.name("output"); + jsonWriter.beginObject(); + writeChildren(nnWriter, (ContainerNode) data); + jsonWriter.endObject(); + } else { + stack.exit(); + + if (data instanceof MapEntryNode) { + data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()) + .withChild((MapEntryNode) data) + .build(); + } + nnWriter = createNormalizedNodeWriter(context, stack.toInference(), jsonWriter, depth); + nnWriter.write(data); + } + nnWriter.flush(); + } + + private static void writeChildren(final RestconfNormalizedNodeWriter nnWriter, final ContainerNode data) + throws IOException { + for (final DataContainerChild child : data.body()) { + nnWriter.write(child); + } + } + + private static RestconfNormalizedNodeWriter createNormalizedNodeWriter( + final InstanceIdentifierContext context, final Inference inference, final JsonWriter jsonWriter, + final @Nullable Integer depth) { + + final SchemaNode schema = context.getSchemaNode(); + final JSONCodecFactory codecs = getCodecFactory(context); + + final XMLNamespace initialNs; + if (schema instanceof DataSchemaNode && !((DataSchemaNode)schema).isAugmenting() + && !(schema instanceof SchemaContext) || schema instanceof RpcDefinition) { + initialNs = schema.getQName().getNamespace(); + } else { + initialNs = null; + } + final NormalizedNodeStreamWriter streamWriter = + JSONNormalizedNodeStreamWriter.createNestedWriter(codecs, inference, initialNs, jsonWriter); + if (depth != null) { + return DepthAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth); + } + + return RestconfDelegatingNormalizedNodeWriter.forStreamWriter(streamWriter); + } + + private static JsonWriter createJsonWriter(final OutputStream entityStream, final boolean prettyPrint) { + if (prettyPrint) { + return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, StandardCharsets.UTF_8), + DEFAULT_INDENT_SPACES_NUM); + } + + return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)); + } + + private static JSONCodecFactory getCodecFactory(final InstanceIdentifierContext context) { + // TODO: Performance: Cache JSON Codec factory and schema context + return JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context.getSchemaContext()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeXmlBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeXmlBodyWriter.java new file mode 100644 index 0000000..e0ba9d9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeXmlBodyWriter.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Map.Entry; +import javanet.staxutils.IndentingXMLStreamWriter; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.XMLConstants; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.util.NetconfUtil; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.xml.sax.SAXException; + +/** + * Normalized node writer for XML. + * + * @deprecated This class will be replaced by NormalizedNodeXmlBodyWriter from restconf-nb-rfc8040 + */ +@Deprecated +@Provider +@Produces({ + Draft02.MediaTypes.API + RestconfService.XML, + Draft02.MediaTypes.DATA + RestconfService.XML, + Draft02.MediaTypes.OPERATION + RestconfService.XML, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML +}) +public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> { + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return type.equals(NormalizedNodeContext.class); + } + + @Override + public long getSize(final NormalizedNodeContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final NormalizedNodeContext context, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, + WebApplicationException { + for (final Entry<String, Object> entry : context.getNewHeaders().entrySet()) { + httpHeaders.add(entry.getKey(), entry.getValue()); + } + final InstanceIdentifierContext pathContext = context.getInstanceIdentifierContext(); + if (context.getData() == null) { + return; + } + + XMLStreamWriter xmlWriter; + try { + xmlWriter = XML_FACTORY.createXMLStreamWriter(entityStream, StandardCharsets.UTF_8.name()); + if (context.getWriterParameters().isPrettyPrint()) { + xmlWriter = new IndentingXMLStreamWriter(xmlWriter); + } + } catch (final XMLStreamException | FactoryConfigurationError e) { + throw new IllegalStateException(e); + } + final NormalizedNode data = context.getData(); + + writeNormalizedNode(xmlWriter, pathContext.inference().toSchemaInferenceStack(), pathContext, data, + context.getWriterParameters().getDepth()); + } + + private static void writeNormalizedNode(final XMLStreamWriter xmlWriter, final SchemaInferenceStack stack, + final InstanceIdentifierContext pathContext, NormalizedNode data, final @Nullable Integer depth) + throws IOException { + final RestconfNormalizedNodeWriter nnWriter; + final EffectiveModelContext schemaCtx = pathContext.getSchemaContext(); + if (stack.isEmpty()) { + nnWriter = createNormalizedNodeWriter(xmlWriter, pathContext.inference(), depth); + if (data instanceof DOMSourceAnyxmlNode) { + try { + writeElements(xmlWriter, nnWriter, + (ContainerNode) NetconfUtil.transformDOMSourceToNormalizedNode(schemaCtx, + ((DOMSourceAnyxmlNode)data).body()).getResult()); + } catch (XMLStreamException | URISyntaxException | SAXException e) { + throw new IOException("Cannot write anyxml", e); + } + } else { + writeElements(xmlWriter, nnWriter, (ContainerNode) data); + } + } else if (pathContext.getSchemaNode() instanceof RpcDefinition) { + final var rpc = (RpcDefinition) pathContext.getSchemaNode(); + final var tmp = SchemaInferenceStack.of(pathContext.getSchemaContext()); + tmp.enterSchemaTree(rpc.getQName()); + tmp.enterSchemaTree(rpc.getOutput().getQName()); + + nnWriter = createNormalizedNodeWriter(xmlWriter, tmp.toInference(), depth); + writeElements(xmlWriter, nnWriter, (ContainerNode) data); + } else { + stack.exit(); + nnWriter = createNormalizedNodeWriter(xmlWriter, stack.toInference(), depth); + if (data instanceof MapEntryNode) { + // Restconf allows returning one list item. We need to wrap it + // in map node in order to serialize it properly + data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()) + .addChild((MapEntryNode) data) + .build(); + } + nnWriter.write(data); + } + nnWriter.flush(); + } + + private static RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter, + final Inference inference, final @Nullable Integer depth) { + final NormalizedNodeStreamWriter xmlStreamWriter = + XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, inference); + if (depth != null) { + return DepthAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth); + } + + return RestconfDelegatingNormalizedNodeWriter.forStreamWriter(xmlStreamWriter); + } + + private static void writeElements(final XMLStreamWriter xmlWriter, final RestconfNormalizedNodeWriter nnWriter, + final ContainerNode data) throws IOException { + final QName name = data.getIdentifier().getNodeType(); + try { + xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(), + name.getNamespace().toString()); + xmlWriter.writeDefaultNamespace(name.getNamespace().toString()); + for (final NormalizedNode child : data.body()) { + nnWriter.write(child); + } + nnWriter.flush(); + xmlWriter.writeEndElement(); + xmlWriter.flush(); + } catch (final XMLStreamException e) { + throw new IOException("Failed to write elements", e); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchJsonBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchJsonBodyWriter.java new file mode 100644 index 0000000..63586af --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchJsonBodyWriter.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.rest.impl; + +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.List; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.patch.PatchStatusEntity; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; + + +@Provider +@Produces({ Draft02.MediaTypes.PATCH_STATUS + RestconfService.JSON }) +public class PatchJsonBodyWriter implements MessageBodyWriter<PatchStatusContext> { + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return type.equals(PatchStatusContext.class); + } + + @Override + public long getSize(final PatchStatusContext patchStatusContext, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final PatchStatusContext patchStatusContext, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) + throws IOException, WebApplicationException { + + final JsonWriter jsonWriter = createJsonWriter(entityStream); + jsonWriter.beginObject().name("ietf-yang-patch:yang-patch-status"); + jsonWriter.beginObject(); + jsonWriter.name("patch-id").value(patchStatusContext.getPatchId()); + if (patchStatusContext.isOk()) { + reportSuccess(jsonWriter); + } else { + if (patchStatusContext.getGlobalErrors() != null) { + reportErrors(patchStatusContext.getGlobalErrors(), jsonWriter); + } + + jsonWriter.name("edit-status"); + jsonWriter.beginObject(); + jsonWriter.name("edit"); + jsonWriter.beginArray(); + for (final PatchStatusEntity patchStatusEntity : patchStatusContext.getEditCollection()) { + jsonWriter.beginObject(); + jsonWriter.name("edit-id").value(patchStatusEntity.getEditId()); + if (patchStatusEntity.getEditErrors() != null) { + reportErrors(patchStatusEntity.getEditErrors(), jsonWriter); + } else { + if (patchStatusEntity.isOk()) { + reportSuccess(jsonWriter); + } + } + jsonWriter.endObject(); + } + jsonWriter.endArray(); + jsonWriter.endObject(); + } + jsonWriter.endObject(); + jsonWriter.endObject(); + jsonWriter.flush(); + } + + private static void reportSuccess(final JsonWriter jsonWriter) throws IOException { + jsonWriter.name("ok").beginArray().nullValue().endArray(); + } + + private static void reportErrors(final List<RestconfError> errors, final JsonWriter jsonWriter) throws IOException { + jsonWriter.name("errors"); + jsonWriter.beginObject(); + jsonWriter.name("error"); + jsonWriter.beginArray(); + + for (final RestconfError restconfError : errors) { + jsonWriter.beginObject(); + jsonWriter.name("error-type").value(restconfError.getErrorType().elementBody()); + jsonWriter.name("error-tag").value(restconfError.getErrorTag().elementBody()); + + // optional node + if (restconfError.getErrorPath() != null) { + jsonWriter.name("error-path").value(restconfError.getErrorPath().toString()); + } + + // optional node + if (restconfError.getErrorMessage() != null) { + jsonWriter.name("error-message").value(restconfError.getErrorMessage()); + } + + // optional node + if (restconfError.getErrorInfo() != null) { + jsonWriter.name("error-info").value(restconfError.getErrorInfo()); + } + + jsonWriter.endObject(); + } + + jsonWriter.endArray(); + jsonWriter.endObject(); + } + + private static JsonWriter createJsonWriter(final OutputStream entityStream) { + return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchXmlBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchXmlBodyWriter.java new file mode 100644 index 0000000..d941d6c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchXmlBodyWriter.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.rest.impl; + +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.List; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.patch.PatchStatusEntity; + +@Provider +@Produces({ Draft02.MediaTypes.PATCH_STATUS + RestconfService.XML }) +public class PatchXmlBodyWriter implements MessageBodyWriter<PatchStatusContext> { + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + @Override + public boolean isWriteable(final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return type.equals(PatchStatusContext.class); + } + + @Override + public long getSize(final PatchStatusContext patchStatusContext, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(final PatchStatusContext patchStatusContext, final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) + throws WebApplicationException { + + try { + final XMLStreamWriter xmlWriter = + XML_FACTORY.createXMLStreamWriter(entityStream, StandardCharsets.UTF_8.name()); + writeDocument(xmlWriter, patchStatusContext); + } catch (final XMLStreamException | FactoryConfigurationError e) { + throw new IllegalStateException(e); + } + } + + private static void writeDocument(final XMLStreamWriter writer, final PatchStatusContext context) + throws XMLStreamException { + writer.writeStartElement("", "yang-patch-status", "urn:ietf:params:xml:ns:yang:ietf-yang-patch"); + writer.writeStartElement("patch-id"); + writer.writeCharacters(context.getPatchId()); + writer.writeEndElement(); + + if (context.isOk()) { + writer.writeEmptyElement("ok"); + } else { + if (context.getGlobalErrors() != null) { + reportErrors(context.getGlobalErrors(), writer); + } + writer.writeStartElement("edit-status"); + for (final PatchStatusEntity patchStatusEntity : context.getEditCollection()) { + writer.writeStartElement("edit"); + writer.writeStartElement("edit-id"); + writer.writeCharacters(patchStatusEntity.getEditId()); + writer.writeEndElement(); + if (patchStatusEntity.getEditErrors() != null) { + reportErrors(patchStatusEntity.getEditErrors(), writer); + } else { + if (patchStatusEntity.isOk()) { + writer.writeEmptyElement("ok"); + } + } + writer.writeEndElement(); + } + writer.writeEndElement(); + + } + writer.writeEndElement(); + + writer.flush(); + } + + private static void reportErrors(final List<RestconfError> errors, final XMLStreamWriter writer) + throws XMLStreamException { + writer.writeStartElement("errors"); + + for (final RestconfError restconfError : errors) { + writer.writeStartElement("error-type"); + writer.writeCharacters(restconfError.getErrorType().elementBody()); + writer.writeEndElement(); + + writer.writeStartElement("error-tag"); + writer.writeCharacters(restconfError.getErrorTag().elementBody()); + writer.writeEndElement(); + + // optional node + if (restconfError.getErrorPath() != null) { + writer.writeStartElement("error-path"); + writer.writeCharacters(restconfError.getErrorPath().toString()); + writer.writeEndElement(); + } + + // optional node + if (restconfError.getErrorMessage() != null) { + writer.writeStartElement("error-message"); + writer.writeCharacters(restconfError.getErrorMessage()); + writer.writeEndElement(); + } + + // optional node + if (restconfError.getErrorInfo() != null) { + writer.writeStartElement("error-info"); + writer.writeCharacters(restconfError.getErrorInfo()); + writer.writeEndElement(); + } + } + + writer.writeEndElement(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfApplication.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfApplication.java new file mode 100644 index 0000000..d8918df --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfApplication.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import com.google.common.collect.ImmutableSet; +import java.util.HashSet; +import java.util.Set; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.core.Application; +import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYangBodyWriter; +import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYinBodyWriter; +import org.opendaylight.netconf.md.sal.rest.schema.SchemaRetrievalServiceImpl; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.StatisticsRestconfServiceWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +@Deprecated(since = "2.0.12") +public class RestconfApplication extends Application { + private static final Logger LOG = LoggerFactory.getLogger(RestconfApplication.class); + + private final ControllerContext controllerContext; + private final StatisticsRestconfServiceWrapper statsServiceWrapper; + + @Inject + public RestconfApplication(final ControllerContext controllerContext, + final StatisticsRestconfServiceWrapper statsServiceWrapper) { + this.controllerContext = controllerContext; + this.statsServiceWrapper = statsServiceWrapper; + LOG.warn("Pre-standard version of RESTCONF activated. Please note that this implementation is considered " + + "obsoleve and WILL BE REMOVED IN THE NEXT MAJOR RELEASE. Please use the RFC8040-compliant " + + "implementation instead."); + } + + @Override + public Set<Class<?>> getClasses() { + return ImmutableSet.<Class<?>>builder() + .add(PatchJsonBodyWriter.class) + .add(PatchXmlBodyWriter.class) + .add(NormalizedNodeJsonBodyWriter.class) + .add(NormalizedNodeXmlBodyWriter.class) + .add(SchemaExportContentYinBodyWriter.class) + .add(SchemaExportContentYangBodyWriter.class) + .build(); + } + + @Override + public Set<Object> getSingletons() { + final Set<Object> singletons = new HashSet<>(); + final SchemaRetrievalServiceImpl schemaRetrieval = new SchemaRetrievalServiceImpl(controllerContext); + singletons.add(schemaRetrieval); + singletons.add(new RestconfCompositeWrapper(statsServiceWrapper, schemaRetrieval)); + singletons.add(new RestconfDocumentedExceptionMapper(controllerContext)); + singletons.add(new XmlNormalizedNodeBodyReader(controllerContext)); + singletons.add(new JsonNormalizedNodeBodyReader(controllerContext)); + singletons.add(new XmlToPatchBodyReader(controllerContext)); + singletons.add(new JsonToPatchBodyReader(controllerContext)); +// singletons.add(StructuredDataToXmlProvider.INSTANCE); +// singletons.add(StructuredDataToJsonProvider.INSTANCE); +// singletons.add(JsonToCompositeNodeProvider.INSTANCE); +// singletons.add(XmlToCompositeNodeProvider.INSTANCE); + return singletons; + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java new file mode 100644 index 0000000..90b83a0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static java.util.Objects.requireNonNull; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.netconf.md.sal.rest.schema.SchemaRetrievalService; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.schema.SchemaExportContext; + +public class RestconfCompositeWrapper implements RestconfService, SchemaRetrievalService { + + private final RestconfService restconf; + private final SchemaRetrievalService schema; + + public RestconfCompositeWrapper(final RestconfService restconf, final SchemaRetrievalService schema) { + this.restconf = requireNonNull(restconf); + this.schema = requireNonNull(schema); + } + + @Override + public Object getRoot() { + return this.restconf.getRoot(); + } + + @Override + public NormalizedNodeContext getModules(final UriInfo uriInfo) { + return this.restconf.getModules(uriInfo); + } + + @Override + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { + return this.restconf.getModules(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { + return this.restconf.getModule(identifier, uriInfo); + } + + @Override + public String getOperationsJSON() { + return this.restconf.getOperationsJSON(); + } + + @Override + public String getOperationsXML() { + return this.restconf.getOperationsXML(); + } + + @Override + public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) { + return this.restconf.getOperations(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + return this.restconf.invokeRpc(identifier, payload, uriInfo); + } + + @Override + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + return this.restconf.readConfigurationData(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) { + return this.restconf.readOperationalData(identifier, uriInfo); + } + + @Override + public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + return this.restconf.updateConfigurationData(identifier, payload, uriInfo); + } + + @Override + public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + return this.restconf.createConfigurationData(identifier, payload, uriInfo); + } + + @Override + public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) { + return this.restconf.createConfigurationData(payload, uriInfo); + } + + @Override + public Response deleteConfigurationData(final String identifier) { + return this.restconf.deleteConfigurationData(identifier); + } + + @Override + public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { + return this.restconf.subscribeToStream(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) { + return this.restconf.getAvailableStreams(uriInfo); + } + + @Override + public PatchStatusContext patchConfigurationData(final String identifier, final PatchContext payload, + final UriInfo uriInfo) { + return this.restconf.patchConfigurationData(identifier, payload, uriInfo); + } + + @Override + public PatchStatusContext patchConfigurationData(final PatchContext context, final UriInfo uriInfo) { + return this.restconf.patchConfigurationData(context, uriInfo); + } + + @Override + public SchemaExportContext getSchema(final String mountId) { + return this.schema.getSchema(mountId); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDelegatingNormalizedNodeWriter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDelegatingNormalizedNodeWriter.java new file mode 100644 index 0000000..aaa1ccc --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDelegatingNormalizedNodeWriter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import java.io.IOException; +import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; + +/** + * This class just delegates all of the functionality to Yangtools normalized node writer. + */ +public final class RestconfDelegatingNormalizedNodeWriter implements RestconfNormalizedNodeWriter { + private final NormalizedNodeWriter delegNNWriter; + + private RestconfDelegatingNormalizedNodeWriter(final NormalizedNodeStreamWriter streamWriter, final boolean + orderKeyLeaves) { + this.delegNNWriter = NormalizedNodeWriter.forStreamWriter(streamWriter, orderKeyLeaves); + } + + public static RestconfDelegatingNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) { + return forStreamWriter(writer, true); + } + + public static RestconfDelegatingNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, + final boolean orderKeyLeaves) { + return new RestconfDelegatingNormalizedNodeWriter(writer, orderKeyLeaves); + } + + @Override + public RestconfDelegatingNormalizedNodeWriter write(final NormalizedNode node) throws IOException { + delegNNWriter.write(node); + return this; + } + + @Override + public void flush() throws IOException { + delegNNWriter.flush(); + } + + @Override + public void close() throws IOException { + delegNNWriter.close(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java new file mode 100644 index 0000000..ff0be39 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfDocumentedExceptionMapper.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import com.google.gson.stream.JsonWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import javax.xml.XMLConstants; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.ErrorTags; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.stream.ForwardingNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; +import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class defines an ExceptionMapper that handles RestconfDocumentedExceptions thrown by resource implementations + * and translates appropriately to restconf error response as defined in the RESTCONF RFC draft. + * + * @author Thomas Pantelis + */ +@Provider +public class RestconfDocumentedExceptionMapper implements ExceptionMapper<RestconfDocumentedException> { + + private static final Logger LOG = LoggerFactory.getLogger(RestconfDocumentedExceptionMapper.class); + + private static final XMLOutputFactory XML_FACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + @Context + private HttpHeaders headers; + + private final ControllerContext controllerContext; + + public RestconfDocumentedExceptionMapper(final ControllerContext controllerContext) { + this.controllerContext = requireNonNull(controllerContext); + } + + @Override + public Response toResponse(final RestconfDocumentedException exception) { + + LOG.debug("In toResponse: {}", exception.getMessage()); + + final List<MediaType> mediaTypeList = new ArrayList<>(); + if (headers.getMediaType() != null) { + mediaTypeList.add(headers.getMediaType()); + } + + mediaTypeList.addAll(headers.getAcceptableMediaTypes()); + final MediaType mediaType = mediaTypeList.stream().filter(type -> !type.equals(MediaType.WILDCARD_TYPE)) + .findFirst().orElse(MediaType.APPLICATION_JSON_TYPE); + + LOG.debug("Using MediaType: {}", mediaType); + + final List<RestconfError> errors = exception.getErrors(); + if (errors.isEmpty()) { + // We don't actually want to send any content but, if we don't set any content here, + // the tomcat front-end will send back an html error report. To prevent that, set a + // single space char in the entity. + + return Response.status(exception.getStatus()).type(MediaType.TEXT_PLAIN_TYPE).entity(" ").build(); + } + + final Status status = ErrorTags.statusOf(errors.iterator().next().getErrorTag()); + final var errorsEntry = controllerContext.getRestconfModuleErrorsSchemaNode(); + if (errorsEntry == null) { + return Response.status(status).type(MediaType.TEXT_PLAIN_TYPE).entity(exception.getMessage()).build(); + } + + final var errorsSchemaNode = errorsEntry.getValue(); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> errContBuild = + SchemaAwareBuilders.containerBuilder(errorsSchemaNode); + + final var schemaList = ControllerContext.findInstanceDataChildrenByName(errorsSchemaNode, + Draft02.RestConfModule.ERROR_LIST_SCHEMA_NODE); + final DataSchemaNode errListSchemaNode = ControllerContext.getFirst(schemaList); + checkState(errListSchemaNode instanceof ListSchemaNode, "Found Error SchemaNode isn't ListSchemaNode"); + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> listErorsBuilder = SchemaAwareBuilders + .mapBuilder((ListSchemaNode) errListSchemaNode); + + + for (final RestconfError error : errors) { + listErorsBuilder.withChild(toErrorEntryNode(error, errListSchemaNode)); + } + errContBuild.withChild(listErorsBuilder.build()); + + final NormalizedNodeContext errContext = new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(errorsEntry.getKey(), null), errContBuild.build()); + + final String responseBody; + if (mediaType.getSubtype().endsWith("json")) { + responseBody = toJsonResponseBody(errContext); + } else { + responseBody = toXMLResponseBody(errContext); + } + + return Response.status(status).type(mediaType).entity(responseBody).build(); + } + + private static MapEntryNode toErrorEntryNode(final RestconfError error, final DataSchemaNode errListSchemaNode) { + checkArgument(errListSchemaNode instanceof ListSchemaNode, + "errListSchemaNode has to be of type ListSchemaNode"); + final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) errListSchemaNode; + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> errNodeValues = SchemaAwareBuilders + .mapEntryBuilder(listStreamSchemaNode); + + var lsChildDataSchemaNode = ControllerContext.findInstanceDataChildrenByName( + listStreamSchemaNode, "error-type"); + final DataSchemaNode errTypSchemaNode = ControllerContext.getFirst(lsChildDataSchemaNode); + checkState(errTypSchemaNode instanceof LeafSchemaNode); + errNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) errTypSchemaNode) + .withValue(error.getErrorType().elementBody()).build()); + + lsChildDataSchemaNode = ControllerContext.findInstanceDataChildrenByName( + listStreamSchemaNode, "error-tag"); + final DataSchemaNode errTagSchemaNode = ControllerContext.getFirst(lsChildDataSchemaNode); + checkState(errTagSchemaNode instanceof LeafSchemaNode); + errNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) errTagSchemaNode) + .withValue(error.getErrorTag().elementBody()).build()); + + if (error.getErrorAppTag() != null) { + lsChildDataSchemaNode = ControllerContext.findInstanceDataChildrenByName( + listStreamSchemaNode, "error-app-tag"); + final DataSchemaNode errAppTagSchemaNode = ControllerContext.getFirst(lsChildDataSchemaNode); + checkState(errAppTagSchemaNode instanceof LeafSchemaNode); + errNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) errAppTagSchemaNode) + .withValue(error.getErrorAppTag()).build()); + } + + lsChildDataSchemaNode = ControllerContext.findInstanceDataChildrenByName( + listStreamSchemaNode, "error-message"); + final DataSchemaNode errMsgSchemaNode = ControllerContext.getFirst(lsChildDataSchemaNode); + checkState(errMsgSchemaNode instanceof LeafSchemaNode); + errNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) errMsgSchemaNode) + .withValue(error.getErrorMessage()).build()); + + if (error.getErrorInfo() != null) { + // Oddly, error-info is defined as an empty container in the restconf yang. Apparently the + // intention is for implementors to define their own data content so we'll just treat it as a leaf + // with string data. + errNodeValues.withChild(ImmutableNodes.leafNode(Draft02.RestConfModule.ERROR_INFO_QNAME, + error.getErrorInfo())); + } + + // TODO : find how could we add possible "error-path" + + return errNodeValues.build(); + } + + private static String toJsonResponseBody(final NormalizedNodeContext errorsNode) { + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + NormalizedNode data = errorsNode.getData(); + final InstanceIdentifierContext context = errorsNode.getInstanceIdentifierContext(); + final DataSchemaNode schema = (DataSchemaNode) context.getSchemaNode(); + + final OutputStreamWriter outputWriter = new OutputStreamWriter(outStream, StandardCharsets.UTF_8); + if (data == null) { + throw new RestconfDocumentedException(Response.Status.NOT_FOUND); + } + + final boolean isDataRoot; + final var stack = context.inference().toSchemaInferenceStack(); + if (stack.isEmpty()) { + isDataRoot = true; + } else { + isDataRoot = false; + stack.exit(); + // FIXME: Add proper handling of reading root. + } + + XMLNamespace initialNs = null; + if (!schema.isAugmenting() && !(schema instanceof SchemaContext)) { + initialNs = schema.getQName().getNamespace(); + } + + final JsonWriter jsonWriter = JsonWriterFactory.createJsonWriter(outputWriter); + final NormalizedNodeStreamWriter jsonStreamWriter = JSONNormalizedNodeStreamWriter.createExclusiveWriter( + JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(context.getSchemaContext()), + stack.toInference(), initialNs, jsonWriter); + + // We create a delegating writer to special-case error-info as error-info is defined as an empty + // container in the restconf yang schema but we create a leaf node so we can output it. The delegate + // stream writer validates the node type against the schema and thus will expect a LeafSchemaNode but + // the schema has a ContainerSchemaNode so, to avoid an error, we override the leafNode behavior + // for error-info. + final NormalizedNodeStreamWriter streamWriter = new ForwardingNormalizedNodeStreamWriter() { + private boolean inOurLeaf; + + @Override + protected NormalizedNodeStreamWriter delegate() { + return jsonStreamWriter; + } + + @Override + public void startLeafNode(final NodeIdentifier name) throws IOException { + if (name.getNodeType().equals(Draft02.RestConfModule.ERROR_INFO_QNAME)) { + inOurLeaf = true; + jsonWriter.name(Draft02.RestConfModule.ERROR_INFO_QNAME.getLocalName()); + } else { + super.startLeafNode(name); + } + } + + @Override + public void scalarValue(final Object value) throws IOException { + if (inOurLeaf) { + jsonWriter.value(value.toString()); + } else { + super.scalarValue(value); + } + } + + @Override + public void endNode() throws IOException { + if (inOurLeaf) { + inOurLeaf = false; + } else { + super.endNode(); + } + } + }; + + final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(streamWriter); + try { + if (isDataRoot) { + writeDataRoot(outputWriter,nnWriter,(ContainerNode) data); + } else { + if (data instanceof MapEntryNode) { + data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()) + .withChild((MapEntryNode) data) + .build(); + } + nnWriter.write(data); + } + nnWriter.flush(); + outputWriter.flush(); + } catch (final IOException e) { + LOG.warn("Error writing error response body", e); + } + + try { + streamWriter.close(); + } catch (IOException e) { + LOG.warn("Failed to close stream writer", e); + } + + return outStream.toString(StandardCharsets.UTF_8); + } + + private static String toXMLResponseBody(final NormalizedNodeContext errorsNode) { + final InstanceIdentifierContext pathContext = errorsNode.getInstanceIdentifierContext(); + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + + final XMLStreamWriter xmlWriter; + try { + xmlWriter = XML_FACTORY.createXMLStreamWriter(outStream, StandardCharsets.UTF_8.name()); + } catch (final XMLStreamException | FactoryConfigurationError e) { + throw new IllegalStateException(e); + } + NormalizedNode data = errorsNode.getData(); + + final boolean isDataRoot; + final var stack = pathContext.inference().toSchemaInferenceStack(); + if (stack.isEmpty()) { + isDataRoot = true; + } else { + isDataRoot = false; + stack.exit(); + } + + final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, + stack.toInference()); + + // We create a delegating writer to special-case error-info as error-info is defined as an empty + // container in the restconf yang schema but we create a leaf node so we can output it. The delegate + // stream writer validates the node type against the schema and thus will expect a LeafSchemaNode but + // the schema has a ContainerSchemaNode so, to avoid an error, we override the leafNode behavior + // for error-info. + final NormalizedNodeStreamWriter streamWriter = new ForwardingNormalizedNodeStreamWriter() { + private boolean inOurLeaf; + + @Override + protected NormalizedNodeStreamWriter delegate() { + return xmlStreamWriter; + } + + @Override + public void startLeafNode(final NodeIdentifier name) throws IOException { + if (name.getNodeType().equals(Draft02.RestConfModule.ERROR_INFO_QNAME)) { + String ns = Draft02.RestConfModule.ERROR_INFO_QNAME.getNamespace().toString(); + try { + xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, + Draft02.RestConfModule.ERROR_INFO_QNAME.getLocalName(), ns); + } catch (XMLStreamException e) { + throw new IOException("Error writing error-info", e); + } + inOurLeaf = true; + } else { + super.startLeafNode(name); + } + } + + @Override + public void scalarValue(final Object value) throws IOException { + if (inOurLeaf) { + try { + xmlWriter.writeCharacters(value.toString()); + } catch (XMLStreamException e) { + throw new IOException("Error writing error-info", e); + } + } else { + super.scalarValue(value); + } + } + + @Override + public void endNode() throws IOException { + if (inOurLeaf) { + try { + xmlWriter.writeEndElement(); + } catch (XMLStreamException e) { + throw new IOException("Error writing error-info", e); + } + inOurLeaf = false; + } else { + super.endNode(); + } + } + }; + + final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(streamWriter); + try { + if (isDataRoot) { + writeRootElement(xmlWriter, nnWriter, (ContainerNode) data); + } else { + if (data instanceof MapEntryNode) { + // Restconf allows returning one list item. We need to wrap it + // in map node in order to serialize it properly + data = ImmutableNodes.mapNodeBuilder(data.getIdentifier().getNodeType()) + .addChild((MapEntryNode) data) + .build(); + } + nnWriter.write(data); + nnWriter.flush(); + } + } catch (final IOException e) { + LOG.warn("Error writing error response body.", e); + } + + return outStream.toString(StandardCharsets.UTF_8); + } + + private static void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, + final ContainerNode data) throws IOException { + final QName name = SchemaContext.NAME; + try { + xmlWriter.writeStartElement(name.getNamespace().toString(), name.getLocalName()); + for (final DataContainerChild child : data.body()) { + nnWriter.write(child); + } + nnWriter.flush(); + xmlWriter.writeEndElement(); + xmlWriter.flush(); + } catch (final XMLStreamException e) { + throw new IOException("Failed to write elements", e); + } + } + + private static void writeDataRoot(final OutputStreamWriter outputWriter, final NormalizedNodeWriter nnWriter, + final ContainerNode data) throws IOException { + for (final DataContainerChild child : data.body()) { + nnWriter.write(child); + nnWriter.flush(); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/StringModuleInstanceIdentifierCodec.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/StringModuleInstanceIdentifierCodec.java new file mode 100644 index 0000000..b61ed58 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/StringModuleInstanceIdentifierCodec.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static java.util.Objects.requireNonNull; + +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Codec for module instance identifiers. + * + * @deprecated This class will be replaced by StringModuleInstanceIdentifierCodec from restconf-nb-rfc8040 + */ +@Deprecated +public final class StringModuleInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec { + + private final DataSchemaContextTree dataContextTree; + private final SchemaContext context; + private final String defaultPrefix; + + public StringModuleInstanceIdentifierCodec(final EffectiveModelContext context) { + this.context = requireNonNull(context); + this.dataContextTree = DataSchemaContextTree.from(context); + this.defaultPrefix = ""; + } + + StringModuleInstanceIdentifierCodec(final EffectiveModelContext context, final @NonNull String defaultPrefix) { + this.context = requireNonNull(context); + this.dataContextTree = DataSchemaContextTree.from(context); + this.defaultPrefix = defaultPrefix; + } + + @Override + protected Module moduleForPrefix(final String prefix) { + if (prefix.isEmpty() && !this.defaultPrefix.isEmpty()) { + return this.context.findModules(this.defaultPrefix).stream().findFirst().orElse(null); + } else { + return this.context.findModules(prefix).stream().findFirst().orElse(null); + } + } + + @Override + protected DataSchemaContextTree getDataContextTree() { + return this.dataContextTree; + } + + @Override + protected String prefixForNamespace(final XMLNamespace namespace) { + return this.context.findModules(namespace).stream().findFirst().map(Module::getName).orElse(null); + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/UnsupportedFormatException.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/UnsupportedFormatException.java new file mode 100644 index 0000000..9e21659 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/UnsupportedFormatException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +public class UnsupportedFormatException extends Exception { + + private static final long serialVersionUID = -1741388894406313402L; + + public UnsupportedFormatException() { + } + + public UnsupportedFormatException(String message, Throwable cause) { + super(message, cause); + } + + public UnsupportedFormatException(String message) { + super(message); + } + + public UnsupportedFormatException(Throwable cause) { + super(cause); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/WriterParameters.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/WriterParameters.java new file mode 100644 index 0000000..1ad6985 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/WriterParameters.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +@Deprecated(forRemoval = true, since = "2.0.6") +public final class WriterParameters { + static final WriterParameters EMPTY = new WriterParametersBuilder().build(); + + private final Integer depth; + private final boolean prettyPrint; + + private WriterParameters(final WriterParametersBuilder builder) { + depth = builder.depth; + prettyPrint = builder.prettyPrint; + } + + public Integer getDepth() { + return depth; + } + + public boolean isPrettyPrint() { + return prettyPrint; + } + + @Deprecated(forRemoval = true, since = "2.0.6") + public static final class WriterParametersBuilder { + private Integer depth; + private boolean prettyPrint; + + public WriterParametersBuilder setDepth(final int depth) { + this.depth = depth; + return this; + } + + public WriterParametersBuilder setPrettyPrint(final boolean prettyPrint) { + this.prettyPrint = prettyPrint; + return this; + } + + public WriterParameters build() { + return new WriterParameters(this); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java new file mode 100644 index 0000000..84a7978 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static com.google.common.base.Preconditions.checkState; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +@Provider +@Consumes({ + Draft02.MediaTypes.DATA + RestconfService.XML, + Draft02.MediaTypes.OPERATION + RestconfService.XML, + MediaType.APPLICATION_XML, + MediaType.TEXT_XML +}) +public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider + implements MessageBodyReader<NormalizedNodeContext> { + + private static final Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class); + + public XmlNormalizedNodeBodyReader(final ControllerContext controllerContext) { + super(controllerContext); + } + + @Override + public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType) { + return true; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public NormalizedNodeContext readFrom(final Class<NormalizedNodeContext> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws + WebApplicationException { + try { + return readFrom(entityStream); + } catch (final RestconfDocumentedException e) { + throw e; + } catch (final Exception e) { + LOG.debug("Error parsing xml input", e); + RestconfDocumentedException.throwIfYangError(e); + throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE, e); + } + } + + private NormalizedNodeContext readFrom(final InputStream entityStream) throws IOException, SAXException, + XMLStreamException, ParserConfigurationException, URISyntaxException { + final InstanceIdentifierContext path = getInstanceIdentifierContext(); + final Optional<InputStream> nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (nonEmptyInputStreamOptional.isEmpty()) { + // represent empty nopayload input + return new NormalizedNodeContext(path, null); + } + + final Document doc = UntrustedXML.newDocumentBuilder().parse(nonEmptyInputStreamOptional.get()); + return parse(path, doc); + } + + private NormalizedNodeContext parse(final InstanceIdentifierContext pathContext,final Document doc) + throws XMLStreamException, IOException, SAXException, URISyntaxException { + final SchemaNode schemaNodeContext = pathContext.getSchemaNode(); + DataSchemaNode schemaNode; + final List<PathArgument> iiToDataList = new ArrayList<>(); + Inference inference; + if (schemaNodeContext instanceof RpcDefinition) { + schemaNode = ((RpcDefinition) schemaNodeContext).getInput(); + inference = pathContext.inference(); + } else if (schemaNodeContext instanceof DataSchemaNode) { + schemaNode = (DataSchemaNode) schemaNodeContext; + + final String docRootElm = doc.getDocumentElement().getLocalName(); + final XMLNamespace docRootNamespace = XMLNamespace.of(doc.getDocumentElement().getNamespaceURI()); + + if (isPost()) { + final var context = pathContext.getSchemaContext(); + final var it = context.findModuleStatements(docRootNamespace).iterator(); + checkState(it.hasNext(), "Failed to find module for %s", docRootNamespace); + final var qname = QName.create(it.next().localQNameModule(), docRootElm); + + final var nodeAndStack = DataSchemaContextTree.from(context) + .enterPath(pathContext.getInstanceIdentifier()).orElseThrow(); + + final var stack = nodeAndStack.stack(); + var current = nodeAndStack.node(); + do { + final var next = current.enterChild(stack, qname); + checkState(next != null, "Child \"%s\" was not found in parent schema node \"%s\"", qname, + schemaNode); + iiToDataList.add(next.getIdentifier()); + schemaNode = next.getDataSchemaNode(); + current = next; + } while (current.isMixin()); + + // We need to unwind the last identifier if it a NodeIdentifierWithPredicates, as it does not have + // any predicates at all. The real identifier is then added below + if (stack.currentStatement() instanceof ListEffectiveStatement) { + iiToDataList.remove(iiToDataList.size() - 1); + } + + inference = stack.toInference(); + + } else { + // PUT + final QName scQName = schemaNode.getQName(); + checkState(docRootElm.equals(scQName.getLocalName()) && docRootNamespace.equals(scQName.getNamespace()), + "Not correct message root element \"%s\", should be \"%s\"", docRootElm, scQName); + inference = pathContext.inference(); + } + } else { + throw new IllegalStateException("Unknown SchemaNode"); + } + + + NormalizedNode parsed; + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + + if (schemaNode instanceof ContainerLike || schemaNode instanceof ListSchemaNode + || schemaNode instanceof LeafSchemaNode) { + final XmlParserStream xmlParser = XmlParserStream.create(writer, inference); + xmlParser.traverse(new DOMSource(doc.getDocumentElement())); + parsed = resultHolder.getResult(); + + // When parsing an XML source with a list root node + // the new XML parser always returns a MapNode with one MapEntryNode inside. + // However, the old XML parser returned a MapEntryNode directly in this place. + // Therefore we now have to extract the MapEntryNode from the parsed MapNode. + if (parsed instanceof MapNode) { + final MapNode mapNode = (MapNode) parsed; + // extracting the MapEntryNode + parsed = mapNode.body().iterator().next(); + } + + if (schemaNode instanceof ListSchemaNode && isPost()) { + iiToDataList.add(parsed.getIdentifier()); + } + } else { + LOG.warn("Unknown schema node extension {} was not parsed", schemaNode.getClass()); + parsed = null; + } + + return new NormalizedNodeContext(pathContext.withConcatenatedArgs(iiToDataList), parsed); + } +} + diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java new file mode 100644 index 0000000..9cc4766 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static com.google.common.base.Verify.verify; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import javax.ws.rs.Consumes; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.dom.DOMSource; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchEditOperation; +import org.opendaylight.restconf.common.patch.PatchEntity; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * Yang PATCH Reader for XML. + * + * @deprecated This class will be replaced by XmlToPatchBodyReader from restconf-nb-rfc8040 + */ +@Deprecated +@Provider +@Consumes({Draft02.MediaTypes.PATCH + RestconfService.XML}) +public class XmlToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider implements + MessageBodyReader<PatchContext> { + + private static final Logger LOG = LoggerFactory.getLogger(XmlToPatchBodyReader.class); + + public XmlToPatchBodyReader(final ControllerContext controllerContext) { + super(controllerContext); + } + + @Override + public boolean isReadable(final Class<?> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType) { + return true; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public PatchContext readFrom(final Class<PatchContext> type, final Type genericType, + final Annotation[] annotations, final MediaType mediaType, + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) + throws WebApplicationException { + + try { + final InstanceIdentifierContext path = getInstanceIdentifierContext(); + final Optional<InputStream> nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (nonEmptyInputStreamOptional.isEmpty()) { + // represent empty nopayload input + return new PatchContext(path, null, null); + } + + final Document doc = UntrustedXML.newDocumentBuilder().parse(nonEmptyInputStreamOptional.get()); + return parse(path, doc); + } catch (final RestconfDocumentedException e) { + throw e; + } catch (final Exception e) { + LOG.debug("Error parsing xml input", e); + RestconfDocumentedException.throwIfYangError(e); + throw new RestconfDocumentedException("Error parsing input: " + e.getMessage(), ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE, e); + } + } + + private static PatchContext parse(final InstanceIdentifierContext pathContext, final Document doc) + throws XMLStreamException, IOException, ParserConfigurationException, SAXException, URISyntaxException { + final List<PatchEntity> resultCollection = new ArrayList<>(); + final String patchId = doc.getElementsByTagName("patch-id").item(0).getFirstChild().getNodeValue(); + final NodeList editNodes = doc.getElementsByTagName("edit"); + + for (int i = 0; i < editNodes.getLength(); i++) { + DataSchemaNode schemaNode = (DataSchemaNode) pathContext.getSchemaNode(); + final Element element = (Element) editNodes.item(i); + final String operation = element.getElementsByTagName("operation").item(0).getFirstChild().getNodeValue(); + final PatchEditOperation oper = PatchEditOperation.valueOf(operation.toUpperCase(Locale.ROOT)); + + final String editId = element.getElementsByTagName("edit-id").item(0).getFirstChild().getNodeValue(); + final String target = element.getElementsByTagName("target").item(0).getFirstChild().getNodeValue(); + final List<Element> values = readValueNodes(element, oper); + final Element firstValueElement = values != null ? values.get(0) : null; + + // get namespace according to schema node from path context or value + final String namespace = firstValueElement == null + ? schemaNode.getQName().getNamespace().toString() : firstValueElement.getNamespaceURI(); + + // find module according to namespace + final Module module = pathContext.getSchemaContext().findModules(XMLNamespace.of(namespace)).iterator() + .next(); + + // initialize codec + set default prefix derived from module name + final StringModuleInstanceIdentifierCodec codec = new StringModuleInstanceIdentifierCodec( + pathContext.getSchemaContext(), module.getName()); + + // find complete path to target and target schema node + // target can be also empty (only slash) + YangInstanceIdentifier targetII; + final SchemaNode targetNode; + final Inference inference; + if (target.equals("/")) { + targetII = pathContext.getInstanceIdentifier(); + targetNode = pathContext.getSchemaContext(); + inference = pathContext.inference(); + } else { + targetII = codec.deserialize(codec.serialize(pathContext.getInstanceIdentifier()) + .concat(prepareNonCondXpath(schemaNode, target.replaceFirst("/", ""), firstValueElement, + namespace, + module.getQNameModule().getRevision().map(Revision::toString).orElse(null)))); + // move schema node + final var result = codec.getDataContextTree().enterPath(targetII).orElseThrow(); + schemaNode = result.node().getDataSchemaNode(); + + final var stack = result.stack(); + inference = stack.toInference(); + + stack.exit(); + final EffectiveStatement<?, ?> parentStmt = stack.currentStatement(); + verify(parentStmt instanceof SchemaNode, "Unexpected parent %s", parentStmt); + targetNode = (SchemaNode) parentStmt; + } + + if (targetNode == null) { + LOG.debug("Target node {} not found in path {} ", target, pathContext.getSchemaNode()); + throw new RestconfDocumentedException("Error parsing input", ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + + if (oper.isWithValue()) { + final NormalizedNode parsed; + if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) { + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + final XmlParserStream xmlParser = XmlParserStream.create(writer, inference); + xmlParser.traverse(new DOMSource(firstValueElement)); + parsed = resultHolder.getResult(); + } else { + parsed = null; + } + + // for lists allow to manipulate with list items through their parent + if (targetII.getLastPathArgument() instanceof NodeIdentifierWithPredicates) { + targetII = targetII.getParent(); + } + + resultCollection.add(new PatchEntity(editId, oper, targetII, parsed)); + } else { + resultCollection.add(new PatchEntity(editId, oper, targetII)); + } + } + + return new PatchContext(pathContext, ImmutableList.copyOf(resultCollection), patchId); + } + + /** + * Read value nodes. + * + * @param element Element of current edit operation + * @param operation Name of current operation + * @return List of value elements + */ + private static List<Element> readValueNodes(final @NonNull Element element, + final @NonNull PatchEditOperation operation) { + final Node valueNode = element.getElementsByTagName("value").item(0); + + if (operation.isWithValue() && valueNode == null) { + throw new RestconfDocumentedException("Error parsing input", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + if (!operation.isWithValue() && valueNode != null) { + throw new RestconfDocumentedException("Error parsing input", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + if (valueNode == null) { + return null; + } + + final List<Element> result = new ArrayList<>(); + final NodeList childNodes = valueNode.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + if (childNodes.item(i) instanceof Element) { + result.add((Element) childNodes.item(i)); + } + } + + return result; + } + + /** + * Prepare non-conditional XPath suitable for deserialization with {@link StringModuleInstanceIdentifierCodec}. + * + * @param schemaNode Top schema node + * @param target Edit operation target + * @param value Element with value + * @param namespace Module namespace + * @param revision Module revision + * @return Non-conditional XPath + */ + private static String prepareNonCondXpath(final @NonNull DataSchemaNode schemaNode, final @NonNull String target, + final @NonNull Element value, final @NonNull String namespace, final @NonNull String revision) { + final Iterator<String> args = Splitter.on("/").split(target.substring(target.indexOf(':') + 1)).iterator(); + + final StringBuilder nonCondXpath = new StringBuilder(); + SchemaNode childNode = schemaNode; + + while (args.hasNext()) { + final String s = args.next(); + nonCondXpath.append('/').append(s); + childNode = ((DataNodeContainer) childNode).getDataChildByName(QName.create(namespace, revision, s)); + + if (childNode instanceof ListSchemaNode && args.hasNext()) { + appendKeys(nonCondXpath, ((ListSchemaNode) childNode).getKeyDefinition().iterator(), args); + } + } + + if (childNode instanceof ListSchemaNode && value != null) { + final Iterator<String> keyValues = readKeyValues(value, + ((ListSchemaNode) childNode).getKeyDefinition().iterator()); + appendKeys(nonCondXpath, ((ListSchemaNode) childNode).getKeyDefinition().iterator(), keyValues); + } + + return nonCondXpath.toString(); + } + + /** + * Read value for every list key. + * + * @param value Value element + * @param keys Iterator of list keys names + * @return Iterator of list keys values + */ + private static Iterator<String> readKeyValues(final @NonNull Element value, final @NonNull Iterator<QName> keys) { + final List<String> result = new ArrayList<>(); + + while (keys.hasNext()) { + result.add(value.getElementsByTagName(keys.next().getLocalName()).item(0).getFirstChild().getNodeValue()); + } + + return result.iterator(); + } + + /** + * Append key name - key value pairs for every list key to {@code nonCondXpath}. + * + * @param nonCondXpath Builder for creating non-conditional XPath + * @param keyNames Iterator of list keys names + * @param keyValues Iterator of list keys values + */ + private static void appendKeys(final @NonNull StringBuilder nonCondXpath, final @NonNull Iterator<QName> keyNames, + final @NonNull Iterator<String> keyValues) { + while (keyNames.hasNext()) { + nonCondXpath.append("[").append(keyNames.next().getLocalName()).append("='").append(keyValues.next()) + .append("']"); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/package-info.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/package-info.java new file mode 100644 index 0000000..07eb8e2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/package-info.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl;
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/JSONRestconfService.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/JSONRestconfService.java new file mode 100644 index 0000000..5e3426c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/JSONRestconfService.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.api; + +import java.util.Optional; +import javax.ws.rs.core.MultivaluedMap; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.common.OperationFailedException; + +/** + * Provides restconf CRUD operations via code with input/output data in JSON format. + * + * @author Thomas Pantelis. + */ +public interface JSONRestconfService { + /** + * The data tree root path. + */ + String ROOT_PATH = null; + + /** + * Issues a restconf PUT request to the configuration data store. + * + * @param uriPath the yang instance identifier path, eg "opendaylight-inventory:nodes/node/device-id". + * To specify the root, use {@link ROOT_PATH}. + * @param payload the payload data in JSON format. + * @throws OperationFailedException if the request fails. + */ + void put(String uriPath, @NonNull String payload) throws OperationFailedException; + + /** + * Issues a restconf POST request to the configuration data store. + * + * @param uriPath the yang instance identifier path, eg "opendaylight-inventory:nodes/node/device-id". + * To specify the root, use {@link ROOT_PATH}. + * @param payload the payload data in JSON format. + * @throws OperationFailedException if the request fails. + */ + void post(String uriPath, @NonNull String payload) throws OperationFailedException; + + /** + * Issues a restconf DELETE request to the configuration data store. + * + * @param uriPath the yang instance identifier path, eg "opendaylight-inventory:nodes/node/device-id". + * To specify the root, use {@link ROOT_PATH}. + * @throws OperationFailedException if the request fails. + */ + void delete(String uriPath) throws OperationFailedException; + + /** + * Issues a restconf GET request to the given data store. + * + * @param uriPath the yang instance identifier path, eg "opendaylight-inventory:nodes/node/device-id". + * To specify the root, use {@link ROOT_PATH}. + * @param datastoreType the data store type to read from. + * @return an Optional containing the data in JSON format if present. + * @throws OperationFailedException if the request fails. + */ + Optional<String> get(String uriPath, LogicalDatastoreType datastoreType) + throws OperationFailedException; + + /** + * Invokes a yang-defined RPC. + * + * @param uriPath the path representing the RPC to invoke, eg "toaster:make-toast". + * @param input the input in JSON format if the RPC takes input. + * @return an Optional containing the output in JSON format if the RPC returns output. + * @throws OperationFailedException if the request fails. + */ + Optional<String> invokeRpc(@NonNull String uriPath, Optional<String> input) throws OperationFailedException; + + /** + * Issues a restconf PATCH request to the configuration data store. + * + * @param uriPath the yang instance identifier path, eg "opendaylight-inventory:nodes/node/device-id". + * To specify the root, use {@link ROOT_PATH}. + * @param payload the payload data in JSON format. + * @return an Optional containing the patch response data in JSON format. + * @throws OperationFailedException if the request fails. + */ + Optional<String> patch(@NonNull String uriPath, @NonNull String payload) throws OperationFailedException; + + /** + * Subscribe to a stream. + * @param identifier the identifier of the stream, e.g., "data-change-event-subscription/neutron:neutron/... + * ...neutron:ports/datastore=OPERATIONAL/scope=SUBTREE". + * @param params HTTP query parameters or null. + * @return On optional containing the JSON response. + * @throws OperationFailedException if the requests fails. + */ + Optional<String> subscribeToStream(@NonNull String identifier, MultivaluedMap<String, String> params) + throws OperationFailedException; +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/RestConfConfig.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/RestConfConfig.java new file mode 100644 index 0000000..169a69d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/api/RestConfConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.api; + +import java.net.InetAddress; + +/** + * Configuration for the RESTCONF server. + * + * @author Michael Vorburger.ch + */ +public interface RestConfConfig { + + /** + * IP interface which the WebSocket server will listen on. + */ + default InetAddress webSocketAddress() { + return InetAddress.getLoopbackAddress(); + } + + /** + * TCP port which the WebSocket server will listen on. + */ + int webSocketPort(); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BatchedExistenceCheck.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BatchedExistenceCheck.java new file mode 100644 index 0000000..4efc6a8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BatchedExistenceCheck.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +final class BatchedExistenceCheck { + private static final AtomicIntegerFieldUpdater<BatchedExistenceCheck> UPDATER = + AtomicIntegerFieldUpdater.newUpdater(BatchedExistenceCheck.class, "outstanding"); + + private final SettableFuture<Entry<YangInstanceIdentifier, ReadFailedException>> future = SettableFuture.create(); + + @SuppressWarnings("unused") + private volatile int outstanding; + + private BatchedExistenceCheck(final int total) { + this.outstanding = total; + } + + static BatchedExistenceCheck start(final DOMDataTreeReadOperations readTx, + final LogicalDatastoreType datastore, final YangInstanceIdentifier parentPath, + final Collection<? extends NormalizedNode> children) { + final BatchedExistenceCheck ret = new BatchedExistenceCheck(children.size()); + for (NormalizedNode child : children) { + final YangInstanceIdentifier path = parentPath.node(child.getIdentifier()); + readTx.exists(datastore, path).addCallback(new FutureCallback<Boolean>() { + @Override + public void onSuccess(final Boolean result) { + ret.complete(path, result); + } + + @Override + @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE") + public void onFailure(final Throwable throwable) { + final Exception e; + if (throwable instanceof Exception) { + e = (Exception) throwable; + } else { + e = new ExecutionException(throwable); + } + + ret.complete(path, ReadFailedException.MAPPER.apply(e)); + } + }, MoreExecutors.directExecutor()); + } + + return ret; + } + + Entry<YangInstanceIdentifier, ReadFailedException> getFailure() throws InterruptedException { + try { + return future.get(); + } catch (ExecutionException e) { + // This should never happen + throw new IllegalStateException(e); + } + } + + private void complete(final YangInstanceIdentifier childPath, final boolean present) { + final int count = UPDATER.decrementAndGet(this); + if (present) { + future.set(new SimpleImmutableEntry<>(childPath, null)); + } else if (count == 0) { + future.set(null); + } + } + + private void complete(final YangInstanceIdentifier childPath, final ReadFailedException cause) { + UPDATER.decrementAndGet(this); + future.set(new SimpleImmutableEntry<>(childPath, cause)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/Bierman02RestConfWiring.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/Bierman02RestConfWiring.java new file mode 100644 index 0000000..18d19e4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/Bierman02RestConfWiring.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.servlet.ServletException; +import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMMountPointService; +import org.opendaylight.mdsal.dom.api.DOMNotificationService; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.netconf.sal.rest.impl.RestconfApplication; +import org.opendaylight.netconf.sal.restconf.api.RestConfConfig; +import org.opendaylight.netconf.sal.restconf.web.WebInitializer; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yangtools.yang.common.Uint16; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Standalone wiring for RESTCONF. + * + * <p>ACK: Some lines here were originally inspired by the RestConfWiring class in + * opendaylight-simple which in turn was inspired by the CommunityRestConf class + * from lighty.io. The differences include (1) that this class is "pure Java" + * without depending on any binding framework utility classes; (2) we do not mix + * bierman02 and rfc8040 for proper modularity; (3) we simply use {@literal @}Inject + * instead of manual object wiring, where possible. + * + * @author Michael Vorburger.ch (see ACK note for history) + */ +@SuppressWarnings("deprecation") +// NOT @Singleton, to avoid that the blueprint-maven-plugin generates <bean>, which we don't want for this +public class Bierman02RestConfWiring { + + private static final Logger LOG = LoggerFactory.getLogger(Bierman02RestConfWiring.class); + + private final RestconfProviderImpl webSocketServer; + + @Inject + // The point of all the arguments here is simply to make your chosen Dependency Injection (DI) framework init. them + public Bierman02RestConfWiring(final RestConfConfig config, + final DOMSchemaService domSchemaService, final DOMMountPointService domMountPointService, + final DOMRpcService domRpcService, final DOMDataBroker domDataBroker, + final DOMNotificationService domNotificationService, final ControllerContext controllerContext, + final RestconfApplication application, final BrokerFacade broker, final RestconfImpl restconf, + final StatisticsRestconfServiceWrapper stats, final JSONRestconfServiceImpl jsonRestconfServiceImpl, + final WebInitializer webInitializer) { + + // WebSocket + LOG.info("webSocketAddress = {}, webSocketPort = {}", config.webSocketAddress(), config.webSocketPort()); + IpAddress wsIpAddress = IpAddressBuilder.getDefaultInstance(config.webSocketAddress().getHostAddress()); + this.webSocketServer = new RestconfProviderImpl(stats, wsIpAddress, + new PortNumber(Uint16.valueOf(config.webSocketPort()))); + } + + @PostConstruct + public void start() throws ServletException { + this.webSocketServer.start(); + } + + @PreDestroy + public void stop() { + this.webSocketServer.close(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java new file mode 100644 index 0000000..ca867cb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java @@ -0,0 +1,1277 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; +import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION; +import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import java.io.Closeable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.core.Response.Status; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMNotificationListener; +import org.opendaylight.mdsal.dom.api.DOMNotificationService; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchEditOperation; +import org.opendaylight.restconf.common.patch.PatchEntity; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.patch.PatchStatusEntity; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public class BrokerFacade implements Closeable { + private static final Logger LOG = LoggerFactory.getLogger(BrokerFacade.class); + + private final ThreadLocal<Boolean> isMounted = new ThreadLocal<>(); + private final DOMNotificationService domNotification; + private final ControllerContext controllerContext; + private final DOMDataBroker domDataBroker; + + private volatile DOMRpcService rpcService; + + @Inject + public BrokerFacade(final DOMRpcService rpcService, final DOMDataBroker domDataBroker, + final DOMNotificationService domNotification, final ControllerContext controllerContext) { + this.rpcService = requireNonNull(rpcService); + this.domDataBroker = requireNonNull(domDataBroker); + this.domNotification = requireNonNull(domNotification); + this.controllerContext = requireNonNull(controllerContext); + } + + /** + * Factory method. + * + * @deprecated Just use + * {@link #BrokerFacade(DOMRpcService, DOMDataBroker, DOMNotificationService, ControllerContext)} + * constructor instead. + */ + @Deprecated + public static BrokerFacade newInstance(final DOMRpcService rpcService, final DOMDataBroker domDataBroker, + final DOMNotificationService domNotification, final ControllerContext controllerContext) { + return new BrokerFacade(rpcService, domDataBroker, domNotification, controllerContext); + } + + @Override + @PreDestroy + public void close() { + } + + /** + * Read config data by path. + * + * @param path + * path of data + * @return read date + */ + public NormalizedNode readConfigurationData(final YangInstanceIdentifier path) { + return readConfigurationData(path, null); + } + + /** + * Read config data by path. + * + * @param path + * path of data + * @param withDefa + * value of with-defaults parameter + * @return read date + */ + public NormalizedNode readConfigurationData(final YangInstanceIdentifier path, final String withDefa) { + try (DOMDataTreeReadTransaction tx = domDataBroker.newReadOnlyTransaction()) { + return readDataViaTransaction(tx, CONFIGURATION, path, withDefa); + } + } + + /** + * Read config data from mount point by path. + * + * @param mountPoint + * mount point for reading data + * @param path + * path of data + * @return read data + */ + public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) { + return readConfigurationData(mountPoint, path, null); + } + + /** + * Read config data from mount point by path. + * + * @param mountPoint + * mount point for reading data + * @param path + * path of data + * @param withDefa + * value of with-defaults parameter + * @return read data + */ + public NormalizedNode readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path, + final String withDefa) { + final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) { + return readDataViaTransaction(tx, CONFIGURATION, path, withDefa); + } + } + throw dataBrokerUnavailable(path); + } + + /** + * Read operational data by path. + * + * @param path + * path of data + * @return read data + */ + public NormalizedNode readOperationalData(final YangInstanceIdentifier path) { + try (DOMDataTreeReadTransaction tx = domDataBroker.newReadOnlyTransaction()) { + return readDataViaTransaction(tx, OPERATIONAL, path); + } + } + + /** + * Read operational data from mount point by path. + * + * @param mountPoint + * mount point for reading data + * @param path + * path of data + * @return read data + */ + public NormalizedNode readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) { + final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + try (DOMDataTreeReadTransaction tx = domDataBrokerService.get().newReadOnlyTransaction()) { + return readDataViaTransaction(tx, OPERATIONAL, path); + } + } + throw dataBrokerUnavailable(path); + } + + /** + * <b>PUT configuration data</b> + * + * <p> + * Prepare result(status) for PUT operation and PUT data via transaction. + * Return wrapped status and future from PUT. + * + * @param globalSchema + * used by merge parents (if contains list) + * @param path + * path of node + * @param payload + * input data + * @param point + * point + * @param insert + * insert + * @return wrapper of status and future of PUT + */ + public PutResult commitConfigurationDataPut(final EffectiveModelContext globalSchema, + final YangInstanceIdentifier path, final NormalizedNode payload, final String insert, final String point) { + requireNonNull(globalSchema); + requireNonNull(path); + requireNonNull(payload); + + isMounted.set(false); + final DOMDataTreeReadWriteTransaction newReadWriteTransaction = domDataBroker.newReadWriteTransaction(); + final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK + : Status.CREATED; + final FluentFuture<? extends CommitInfo> future = putDataViaTransaction( + newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point); + isMounted.remove(); + return new PutResult(status, future); + } + + /** + * <b>PUT configuration data (Mount point)</b> + * + * <p> + * Prepare result(status) for PUT operation and PUT data via transaction. + * Return wrapped status and future from PUT. + * + * @param mountPoint + * mount point for getting transaction for operation and schema + * context for merging parents(if contains list) + * @param path + * path of node + * @param payload + * input data + * @param point + * point + * @param insert + * insert + * @return wrapper of status and future of PUT + */ + public PutResult commitMountPointDataPut(final DOMMountPoint mountPoint, final YangInstanceIdentifier path, + final NormalizedNode payload, final String insert, final String point) { + requireNonNull(mountPoint); + requireNonNull(path); + requireNonNull(payload); + + isMounted.set(true); + final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + final DOMDataTreeReadWriteTransaction newReadWriteTransaction = + domDataBrokerService.get().newReadWriteTransaction(); + final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null + ? Status.OK : Status.CREATED; + final FluentFuture<? extends CommitInfo> future = putDataViaTransaction( + newReadWriteTransaction, CONFIGURATION, path, payload, modelContext(mountPoint), insert, point); + isMounted.remove(); + return new PutResult(status, future); + } + isMounted.remove(); + throw dataBrokerUnavailable(path); + } + + public PatchStatusContext patchConfigurationDataWithinTransaction(final PatchContext patchContext) + throws Exception { + final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint(); + + // get new transaction and schema context on server or on mounted device + final EffectiveModelContext schemaContext; + final DOMDataTreeReadWriteTransaction patchTransaction; + if (mountPoint == null) { + schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext(); + patchTransaction = domDataBroker.newReadWriteTransaction(); + } else { + schemaContext = modelContext(mountPoint); + + final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class); + + if (optional.isPresent()) { + patchTransaction = optional.get().newReadWriteTransaction(); + } else { + // if mount point does not have broker it is not possible to continue and global error is reported + LOG.error("Http Patch {} has failed - device {} does not support broker service", + patchContext.getPatchId(), mountPoint.getIdentifier()); + return new PatchStatusContext( + patchContext.getPatchId(), + null, + false, + ImmutableList.of(new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, + "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier())) + ); + } + } + + final List<PatchStatusEntity> editCollection = new ArrayList<>(); + List<RestconfError> editErrors; + boolean withoutError = true; + + for (final PatchEntity patchEntity : patchContext.getData()) { + final PatchEditOperation operation = patchEntity.getOperation(); + switch (operation) { + case CREATE: + if (withoutError) { + try { + postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(), + patchEntity.getNode(), schemaContext); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null)); + } catch (final RestconfDocumentedException e) { + LOG.error("Error call http Patch operation {} on target {}", + operation, + patchEntity.getTargetNode().toString()); + + editErrors = new ArrayList<>(); + editErrors.addAll(e.getErrors()); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors)); + withoutError = false; + } + } + break; + case REPLACE: + if (withoutError) { + try { + putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity + .getTargetNode(), patchEntity.getNode(), schemaContext); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null)); + } catch (final RestconfDocumentedException e) { + LOG.error("Error call http Patch operation {} on target {}", + operation, + patchEntity.getTargetNode().toString()); + + editErrors = new ArrayList<>(); + editErrors.addAll(e.getErrors()); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors)); + withoutError = false; + } + } + break; + case DELETE: + case REMOVE: + if (withoutError) { + try { + deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity + .getTargetNode()); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null)); + } catch (final RestconfDocumentedException e) { + LOG.error("Error call http Patch operation {} on target {}", + operation, + patchEntity.getTargetNode().toString()); + + editErrors = new ArrayList<>(); + editErrors.addAll(e.getErrors()); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors)); + withoutError = false; + } + } + break; + case MERGE: + if (withoutError) { + try { + mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(), + patchEntity.getNode(), schemaContext); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null)); + } catch (final RestconfDocumentedException e) { + LOG.error("Error call http Patch operation {} on target {}", + operation, + patchEntity.getTargetNode().toString()); + + editErrors = new ArrayList<>(); + editErrors.addAll(e.getErrors()); + editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), false, editErrors)); + withoutError = false; + } + } + break; + default: + LOG.error("Unsupported http Patch operation {} on target {}", + operation, + patchEntity.getTargetNode().toString()); + break; + } + } + + // if errors then cancel transaction and return error status + if (!withoutError) { + patchTransaction.cancel(); + return new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null); + } + + // if no errors commit transaction + final CountDownLatch waiter = new CountDownLatch(1); + final FluentFuture<? extends CommitInfo> future = patchTransaction.commit(); + final PatchStatusContextHelper status = new PatchStatusContextHelper(); + + future.addCallback(new FutureCallback<CommitInfo>() { + @Override + public void onSuccess(final CommitInfo result) { + status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), + true, null)); + waiter.countDown(); + } + + @Override + public void onFailure(final Throwable throwable) { + // if commit failed it is global error + LOG.error("Http Patch {} transaction commit has failed", patchContext.getPatchId()); + status.setStatus(new PatchStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), + false, ImmutableList.of( + new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, throwable.getMessage())))); + waiter.countDown(); + } + }, MoreExecutors.directExecutor()); + + waiter.await(); + return status.getStatus(); + } + + // POST configuration + public FluentFuture<? extends CommitInfo> commitConfigurationDataPost( + final EffectiveModelContext globalSchema, final YangInstanceIdentifier path, + final NormalizedNode payload, final String insert, final String point) { + isMounted.set(false); + FluentFuture<? extends CommitInfo> future = + postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, + globalSchema, insert, point); + isMounted.remove(); + return future; + } + + public FluentFuture<? extends CommitInfo> commitConfigurationDataPost( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode payload, + final String insert, final String point) { + isMounted.set(true); + final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + FluentFuture<? extends CommitInfo> future = + postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path, + payload, modelContext(mountPoint), insert, point); + isMounted.remove(); + return future; + } + isMounted.remove(); + throw dataBrokerUnavailable(path); + } + + // DELETE configuration + public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete(final YangInstanceIdentifier path) { + return deleteDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path); + } + + public FluentFuture<? extends CommitInfo> commitConfigurationDataDelete( + final DOMMountPoint mountPoint, final YangInstanceIdentifier path) { + final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class); + if (domDataBrokerService.isPresent()) { + return deleteDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path); + } + throw dataBrokerUnavailable(path); + } + + // RPC + public ListenableFuture<? extends DOMRpcResult> invokeRpc(final @NonNull QName type, + final @NonNull NormalizedNode input) { + if (rpcService == null) { + throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE); + } + LOG.trace("Invoke RPC {} with input: {}", type, input); + return rpcService.invokeRpc(type, input); + } + + public void registerToListenDataChanges(final LogicalDatastoreType datastore, final Scope scope, + final ListenerAdapter listener) { + if (listener.isListening()) { + return; + } + + final YangInstanceIdentifier path = listener.getPath(); + DOMDataTreeChangeService changeService = domDataBroker.getExtensions() + .getInstance(DOMDataTreeChangeService.class); + if (changeService == null) { + throw new UnsupportedOperationException("DOMDataBroker does not support the DOMDataTreeChangeService" + + domDataBroker); + } + DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(datastore, path); + ListenerRegistration<ListenerAdapter> registration = + changeService.registerDataTreeChangeListener(root, listener); + listener.setRegistration(registration); + } + + private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path) { + return readDataViaTransaction(transaction, datastore, path, null); + } + + private NormalizedNode readDataViaTransaction(final DOMDataTreeReadOperations transaction, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) { + LOG.trace("Read {} via Restconf: {}", datastore.name(), path); + + try { + final Optional<NormalizedNode> optional = transaction.read(datastore, path).get(); + return optional.map(normalizedNode -> withDefa == null ? normalizedNode : + prepareDataByParamWithDef(normalizedNode, path, withDefa)).orElse(null); + } catch (InterruptedException e) { + LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e); + throw new RestconfDocumentedException("Error reading data.", e); + } catch (ExecutionException e) { + LOG.warn("Error reading {} from datastore {}", path, datastore.name(), e); + throw RestconfDocumentedException.decodeAndThrow("Error reading data.", Throwables.getCauseAs(e, + ReadFailedException.class)); + } + } + + private NormalizedNode prepareDataByParamWithDef(final NormalizedNode result, + final YangInstanceIdentifier path, final String withDefa) { + boolean trim; + switch (withDefa) { + case "trim": + trim = true; + break; + case "explicit": + trim = false; + break; + default: + throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa); + } + + final EffectiveModelContext ctx = controllerContext.getGlobalSchema(); + final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx); + final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode(); + if (result instanceof ContainerNode) { + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) baseSchemaNode); + buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim); + return builder.build(); + } + + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = + SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) baseSchemaNode); + buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim, + ((ListSchemaNode) baseSchemaNode).getKeyDefinition()); + return builder.build(); + } + + private void buildMapEntryBuilder( + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder, + final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree, + final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) { + for (final DataContainerChild child : result.body()) { + final YangInstanceIdentifier path = actualPath.node(child.getIdentifier()); + final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode(); + if (child instanceof ContainerNode) { + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> childBuilder = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema); + buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim); + builder.withChild(childBuilder.build()); + } else if (child instanceof MapNode) { + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder = + SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema); + buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim, + ((ListSchemaNode) childSchema).getKeyDefinition()); + builder.withChild(childBuilder.build()); + } else if (child instanceof LeafNode) { + final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null); + final Object nodeVal = child.body(); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema); + if (keys.contains(child.getIdentifier().getNodeType())) { + leafBuilder.withValue(child.body()); + builder.withChild(leafBuilder.build()); + } else { + if (trim) { + if (defaultVal == null || !defaultVal.equals(nodeVal)) { + leafBuilder.withValue(child.body()); + builder.withChild(leafBuilder.build()); + } + } else { + if (defaultVal != null && defaultVal.equals(nodeVal)) { + leafBuilder.withValue(child.body()); + builder.withChild(leafBuilder.build()); + } + } + } + } + } + } + + private void buildList(final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder, final MapNode result, + final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim, + final List<QName> keys) { + for (final MapEntryNode mapEntryNode : result.body()) { + final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier()); + final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(actualNode).orElseThrow() + .getDataSchemaNode(); + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = + SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) childSchema); + buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys); + builder.withChild(mapEntryBuilder.build()); + } + } + + private void buildCont(final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builder, + final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree, + final YangInstanceIdentifier actualPath, final boolean trim) { + for (final DataContainerChild child : result.body()) { + final YangInstanceIdentifier path = actualPath.node(child.getIdentifier()); + final DataSchemaNode childSchema = baseSchemaCtxTree.findChild(path).orElseThrow().getDataSchemaNode(); + if (child instanceof ContainerNode) { + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> builderChild = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) childSchema); + buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim); + builder.withChild(builderChild.build()); + } else if (child instanceof MapNode) { + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> childBuilder = + SchemaAwareBuilders.mapBuilder((ListSchemaNode) childSchema); + buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim, + ((ListSchemaNode) childSchema).getKeyDefinition()); + builder.withChild(childBuilder.build()); + } else if (child instanceof LeafNode) { + final Object defaultVal = ((LeafSchemaNode) childSchema).getType().getDefaultValue().orElse(null); + final Object nodeVal = child.body(); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) childSchema); + if (trim) { + if (defaultVal == null || !defaultVal.equals(nodeVal)) { + leafBuilder.withValue(child.body()); + builder.withChild(leafBuilder.build()); + } + } else { + if (defaultVal != null && defaultVal.equals(nodeVal)) { + leafBuilder.withValue(child.body()); + builder.withChild(leafBuilder.build()); + } + } + } + } + } + + /** + * POST data and submit transaction {@link DOMDataReadWriteTransaction}. + */ + private FluentFuture<? extends CommitInfo> postDataViaTransaction( + final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String insert, final String point) { + LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload); + postData(rwTransaction, datastore, path, payload, schemaContext, insert, point); + return rwTransaction.commit(); + } + + /** + * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}. + */ + private void postDataWithinTransaction( + final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + LOG.trace("POST {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload); + postData(rwTransaction, datastore, path, payload, schemaContext, null, null); + } + + private void postData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String insert, final String point) { + if (insert == null) { + makeNormalPost(rwTransaction, datastore, path, payload, schemaContext); + return; + } + + final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path); + checkItemDoesNotExists(rwTransaction, datastore, path); + switch (insert) { + case "first": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = + (UserMapNode) this.readConfigurationData(path.getParent().getParent()); + if (readList == null || readList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + rwTransaction.delete(datastore, path.getParent().getParent()); + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readList, + schemaContext); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + rwTransaction.delete(datastore, path.getParent()); + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + makeNormalPost(rwTransaction, datastore, path.getParent().getParent(), readLeafList, + schemaContext); + } + } + break; + case "last": + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + break; + case "before": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = + (UserMapNode) this.readConfigurationData(path.getParent().getParent()); + if (readList == null || readList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point, + readList, + true); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point, + readLeafList, true); + } + } + break; + case "after": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = + (UserMapNode) this.readConfigurationData(path.getParent().getParent()); + if (readList == null || readList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + insertWithPointListPost(rwTransaction, datastore, path, payload, schemaContext, point, + readList, + false); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } else { + insertWithPointLeafListPost(rwTransaction, datastore, path, payload, schemaContext, point, + readLeafList, false); + } + } + break; + default: + throw new RestconfDocumentedException( + "Used bad value of insert parameter. Possible values are first, last, before or after, " + + "but was: " + insert); + } + } + + private void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList, + final boolean before) { + rwTransaction.delete(datastore, path.getParent().getParent()); + final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point); + int lastItemPosition = 0; + for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) { + if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; + } + lastItemPosition++; + } + if (!before) { + lastItemPosition++; + } + int lastInsertedPosition = 0; + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); + rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) { + if (lastInsertedPosition == lastItemPosition) { + checkItemDoesNotExists(rwTransaction, datastore, path); + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } + final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier()); + checkItemDoesNotExists(rwTransaction, datastore, childPath); + rwTransaction.put(datastore, childPath, nodeChild); + lastInsertedPosition++; + } + } + + private void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, final EffectiveModelContext schemaContext, + final String point, final MapNode readList, final boolean before) { + rwTransaction.delete(datastore, path.getParent().getParent()); + final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point); + int lastItemPosition = 0; + for (final MapEntryNode mapEntryNode : readList.body()) { + if (mapEntryNode.getIdentifier() + .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; + } + lastItemPosition++; + } + if (!before) { + lastItemPosition++; + } + int lastInsertedPosition = 0; + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent()); + rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final MapEntryNode mapEntryNode : readList.body()) { + if (lastInsertedPosition == lastItemPosition) { + checkItemDoesNotExists(rwTransaction, datastore, path); + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + } + final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier()); + checkItemDoesNotExists(rwTransaction, datastore, childPath); + rwTransaction.put(datastore, childPath, mapEntryNode); + lastInsertedPosition++; + } + } + + private static DataSchemaNode checkListAndOrderedType(final EffectiveModelContext ctx, + final YangInstanceIdentifier path) { + final YangInstanceIdentifier parent = path.getParent(); + final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).findChild(parent).orElseThrow(); + final DataSchemaNode dataSchemaNode = node.getDataSchemaNode(); + + if (dataSchemaNode instanceof ListSchemaNode) { + if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) { + throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list."); + } + return dataSchemaNode; + } + if (dataSchemaNode instanceof LeafListSchemaNode) { + if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) { + throw new RestconfDocumentedException( + "Insert parameter can be used only with ordered-by user leaf-list."); + } + return dataSchemaNode; + } + throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list"); + } + + private void makeNormalPost(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + final Collection<? extends NormalizedNode> children; + if (payload instanceof MapNode) { + children = ((MapNode) payload).body(); + } else if (payload instanceof LeafSetNode) { + children = ((LeafSetNode<?>) payload).body(); + } else { + simplePostPut(rwTransaction, datastore, path, payload, schemaContext); + return; + } + + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path); + if (children.isEmpty()) { + if (isMounted != null && !isMounted.get()) { + + rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), + emptySubtree); + ensureParentsByMerge(datastore, path, rwTransaction, schemaContext); + } + return; + } + + // Kick off batch existence check first... + final BatchedExistenceCheck check = BatchedExistenceCheck.start(rwTransaction, datastore, path, children); + + // ... now enqueue modifications. This relies on proper ordering of requests, i.e. these will not affect the + // result of the existence checks... + if (isMounted != null && !isMounted.get()) { + + rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + ensureParentsByMerge(datastore, path, rwTransaction, schemaContext); + } + for (final NormalizedNode child : children) { + // FIXME: we really want a create(YangInstanceIdentifier, NormalizedNode) method in the transaction, + // as that would allow us to skip the existence checks + rwTransaction.put(datastore, path.node(child.getIdentifier()), child); + } + + // ... finally collect existence checks and abort the transaction if any of them failed. + final Entry<YangInstanceIdentifier, ReadFailedException> failure; + try { + failure = check.getFailure(); + } catch (InterruptedException e) { + rwTransaction.cancel(); + throw new RestconfDocumentedException("Could not determine the existence of path " + path, e); + } + + if (failure != null) { + rwTransaction.cancel(); + final ReadFailedException e = failure.getValue(); + if (e == null) { + throw new RestconfDocumentedException("Data already exists for path: " + failure.getKey(), + ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS); + } + + throw new RestconfDocumentedException("Could not determine the existence of path " + failure.getKey(), e, + e.getErrorList()); + } + } + + private void simplePostPut(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + checkItemDoesNotExists(rwTransaction, datastore, path); + if (isMounted != null && !isMounted.get()) { + ensureParentsByMerge(datastore, path, rwTransaction, schemaContext); + } + rwTransaction.put(datastore, path, payload); + } + + private static boolean doesItemExist(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType store, final YangInstanceIdentifier path) { + try { + return rwTransaction.exists(store, path).get(); + } catch (InterruptedException e) { + rwTransaction.cancel(); + throw new RestconfDocumentedException("Could not determine the existence of path " + path, e); + } catch (ExecutionException e) { + rwTransaction.cancel(); + throw RestconfDocumentedException.decodeAndThrow("Could not determine the existence of path " + path, + Throwables.getCauseAs(e, ReadFailedException.class)); + } + } + + /** + * Check if item already exists. Throws error if it does NOT already exist. + * @param rwTransaction Current transaction + * @param store Used datastore + * @param path Path to item to verify its existence + */ + private static void checkItemExists(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType store, final YangInstanceIdentifier path) { + if (!doesItemExist(rwTransaction, store, path)) { + LOG.trace("Operation via Restconf was not executed because data at {} does not exist", path); + rwTransaction.cancel(); + throw new RestconfDocumentedException("Data does not exist for path: " + path, ErrorType.PROTOCOL, + ErrorTag.DATA_MISSING); + } + } + + /** + * Check if item does NOT already exist. Throws error if it already exists. + * @param rwTransaction Current transaction + * @param store Used datastore + * @param path Path to item to verify its existence + */ + private static void checkItemDoesNotExists(final DOMDataTreeReadWriteTransaction rwTransaction, + final LogicalDatastoreType store, final YangInstanceIdentifier path) { + if (doesItemExist(rwTransaction, store, path)) { + LOG.trace("Operation via Restconf was not executed because data at {} already exists", path); + rwTransaction.cancel(); + throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL, + ErrorTag.DATA_EXISTS); + } + } + + /** + * PUT data and submit {@link DOMDataReadWriteTransaction}. + * + * @param point + * point + * @param insert + * insert + */ + private FluentFuture<? extends CommitInfo> putDataViaTransaction( + final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String insert, final String point) { + LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload); + putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point); + return readWriteTransaction.commit(); + } + + /** + * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}. + */ + private void putDataWithinTransaction( + final DOMDataTreeReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + LOG.trace("Put {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload); + putData(writeTransaction, datastore, path, payload, schemaContext, null, null); + } + + // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case + private void putData(final DOMDataTreeReadWriteTransaction rwTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String insert, final String point) { + if (insert == null) { + makePut(rwTransaction, datastore, path, payload, schemaContext); + return; + } + + final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path); + checkItemDoesNotExists(rwTransaction, datastore, path); + switch (insert) { + case "first": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent()); + if (readList == null || readList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + rwTransaction.delete(datastore, path.getParent()); + simplePut(datastore, path, rwTransaction, schemaContext, payload); + makePut(rwTransaction, datastore, path.getParent(), readList, schemaContext); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + rwTransaction.delete(datastore, path.getParent()); + simplePut(datastore, path, rwTransaction, schemaContext, payload); + makePut(rwTransaction, datastore, path.getParent(), readLeafList, + schemaContext); + } + } + break; + case "last": + simplePut(datastore, path, rwTransaction, schemaContext, payload); + break; + case "before": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent()); + if (readList == null || readList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point, + readList, true); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point, + readLeafList, true); + } + } + break; + case "after": + if (schemaNode instanceof ListSchemaNode) { + final UserMapNode readList = (UserMapNode) this.readConfigurationData(path.getParent()); + if (readList == null || readList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + insertWithPointListPut(rwTransaction, datastore, path, payload, schemaContext, point, + readList, false); + } + } else { + final UserLeafSetNode<?> readLeafList = + (UserLeafSetNode<?>) readConfigurationData(path.getParent()); + if (readLeafList == null || readLeafList.isEmpty()) { + simplePut(datastore, path, rwTransaction, schemaContext, payload); + } else { + insertWithPointLeafListPut(rwTransaction, datastore, path, payload, schemaContext, point, + readLeafList, false); + } + } + break; + default: + throw new RestconfDocumentedException( + "Used bad value of insert parameter. Possible values are first, last, before or after, but was: " + + insert); + } + } + + private void insertWithPointLeafListPut(final DOMDataTreeWriteTransaction tx, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext, final String point, final UserLeafSetNode<?> readLeafList, + final boolean before) { + tx.delete(datastore, path.getParent()); + final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point); + int index1 = 0; + for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) { + if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; + } + index1++; + } + if (!before) { + index1++; + } + int index2 = 0; + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent()); + tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final LeafSetEntryNode<?> nodeChild : readLeafList.body()) { + if (index2 == index1) { + simplePut(datastore, path, tx, schemaContext, payload); + } + final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier()); + tx.put(datastore, childPath, nodeChild); + index2++; + } + } + + private void insertWithPointListPut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, final EffectiveModelContext schemaContext, + final String point, final UserMapNode readList, final boolean before) { + tx.delete(datastore, path.getParent()); + final InstanceIdentifierContext instanceIdentifier = controllerContext.toInstanceIdentifier(point); + int index1 = 0; + for (final MapEntryNode mapEntryNode : readList.body()) { + if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) { + break; + } + index1++; + } + if (!before) { + index1++; + } + int index2 = 0; + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent()); + tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + for (final MapEntryNode mapEntryNode : readList.body()) { + if (index2 == index1) { + simplePut(datastore, path, tx, schemaContext, payload); + } + final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier()); + tx.put(datastore, childPath, mapEntryNode); + index2++; + } + } + + private void makePut(final DOMDataTreeWriteTransaction tx, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + if (payload instanceof MapNode) { + final NormalizedNode emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path); + if (isMounted != null && !isMounted.get()) { + tx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree); + ensureParentsByMerge(datastore, path, tx, schemaContext); + } + for (final MapEntryNode child : ((MapNode) payload).body()) { + final YangInstanceIdentifier childPath = path.node(child.getIdentifier()); + tx.put(datastore, childPath, child); + } + } else { + simplePut(datastore, path, tx, schemaContext, payload); + } + } + + private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path, + final DOMDataTreeWriteTransaction tx, final EffectiveModelContext schemaContext, + final NormalizedNode payload) { + if (isMounted != null && !isMounted.get()) { + ensureParentsByMerge(datastore, path, tx, schemaContext); + } + tx.put(datastore, path, payload); + } + + private static FluentFuture<? extends CommitInfo> deleteDataViaTransaction( + final DOMDataTreeReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore, + final YangInstanceIdentifier path) { + LOG.trace("Delete {} via Restconf: {}", datastore.name(), path); + checkItemExists(readWriteTransaction, datastore, path); + readWriteTransaction.delete(datastore, path); + return readWriteTransaction.commit(); + } + + private static void deleteDataWithinTransaction(final DOMDataTreeWriteTransaction tx, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path) { + LOG.trace("Delete {} within Restconf Patch: {}", datastore.name(), path); + tx.delete(datastore, path); + } + + private static void mergeDataWithinTransaction(final DOMDataTreeWriteTransaction tx, + final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode payload, + final EffectiveModelContext schemaContext) { + LOG.trace("Merge {} within Restconf Patch: {} with payload {}", datastore.name(), path, payload); + ensureParentsByMerge(datastore, path, tx, schemaContext); + + // Since YANG Patch provides the option to specify what kind of operation for each edit, + // OpenDaylight should not change it. + tx.merge(datastore, path, payload); + } + + public void registerToListenNotification(final NotificationListenerAdapter listener) { + if (listener.isListening()) { + return; + } + + final ListenerRegistration<DOMNotificationListener> registration = domNotification + .registerNotificationListener(listener, listener.getSchemaPath()); + + listener.setRegistration(registration); + } + + private static void ensureParentsByMerge(final LogicalDatastoreType store, + final YangInstanceIdentifier normalizedPath, final DOMDataTreeWriteTransaction tx, + final EffectiveModelContext schemaContext) { + final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>(); + YangInstanceIdentifier rootNormalizedPath = null; + + final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator(); + + while (it.hasNext()) { + final PathArgument pathArgument = it.next(); + if (rootNormalizedPath == null) { + rootNormalizedPath = YangInstanceIdentifier.create(pathArgument); + } + + if (it.hasNext()) { + normalizedPathWithoutChildArgs.add(pathArgument); + } + } + + if (normalizedPathWithoutChildArgs.isEmpty()) { + return; + } + + checkArgument(rootNormalizedPath != null, "Empty path received"); + tx.merge(store, rootNormalizedPath, ImmutableNodes.fromInstanceId(schemaContext, + YangInstanceIdentifier.create(normalizedPathWithoutChildArgs))); + } + + private static RestconfDocumentedException dataBrokerUnavailable(final YangInstanceIdentifier path) { + LOG.warn("DOM data broker service is not available for mount point {}", path); + return new RestconfDocumentedException("DOM data broker service is not available for mount point " + path); + } + + private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { + return mountPoint.getService(DOMSchemaService.class) + .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) + .orElse(null); + } + + private static final class PatchStatusContextHelper { + PatchStatusContext status; + + public PatchStatusContext getStatus() { + return status; + } + + public void setStatus(final PatchStatusContext status) { + this.status = status; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java new file mode 100644 index 0000000..8e05bf1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java @@ -0,0 +1,1006 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.Closeable; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.core.Response.Status; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMMountPointService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.Draft02.RestConfModule; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.concepts.IllegalArgumentCodec; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener; +import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; +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.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public final class ControllerContext implements EffectiveModelContextListener, Closeable { + // FIXME: this should be in md-sal somewhere + public static final String MOUNT = "yang-ext:mount"; + + private static final Logger LOG = LoggerFactory.getLogger(ControllerContext.class); + + private static final String NULL_VALUE = "null"; + + private static final String MOUNT_MODULE = "yang-ext"; + + private static final String MOUNT_NODE = "mount"; + + private static final Splitter SLASH_SPLITTER = Splitter.on('/'); + + private final AtomicReference<Map<QName, RpcDefinition>> qnameToRpc = new AtomicReference<>(Collections.emptyMap()); + + private final DOMMountPointService mountService; + private final DOMYangTextSourceProvider yangTextSourceProvider; + private final ListenerRegistration<?> listenerRegistration; + private volatile EffectiveModelContext globalSchema; + private volatile DataNormalizer dataNormalizer; + + @Inject + public ControllerContext(final DOMSchemaService schemaService, final DOMMountPointService mountService, + final DOMSchemaService domSchemaService) { + this.mountService = mountService; + yangTextSourceProvider = domSchemaService.getExtensions().getInstance(DOMYangTextSourceProvider.class); + + onModelContextUpdated(schemaService.getGlobalContext()); + listenerRegistration = schemaService.registerSchemaContextListener(this); + } + + /** + * Factory method. + * + * @deprecated Just use the + * {@link #ControllerContext(DOMSchemaService, DOMMountPointService, DOMSchemaService)} + * constructor instead. + */ + @Deprecated + public static ControllerContext newInstance(final DOMSchemaService schemaService, + final DOMMountPointService mountService, final DOMSchemaService domSchemaService) { + return new ControllerContext(schemaService, mountService, domSchemaService); + } + + private void setGlobalSchema(final EffectiveModelContext globalSchema) { + this.globalSchema = globalSchema; + dataNormalizer = new DataNormalizer(globalSchema); + } + + public DOMYangTextSourceProvider getYangTextSourceProvider() { + return yangTextSourceProvider; + } + + private void checkPreconditions() { + if (globalSchema == null) { + throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE); + } + } + + @Override + @PreDestroy + public void close() { + listenerRegistration.close(); + } + + public void setSchemas(final EffectiveModelContext schemas) { + onModelContextUpdated(schemas); + } + + public InstanceIdentifierContext toInstanceIdentifier(final String restconfInstance) { + return toIdentifier(restconfInstance, false); + } + + public EffectiveModelContext getGlobalSchema() { + return globalSchema; + } + + public InstanceIdentifierContext toMountPointIdentifier(final String restconfInstance) { + return toIdentifier(restconfInstance, true); + } + + private InstanceIdentifierContext toIdentifier(final String restconfInstance, + final boolean toMountPointIdentifier) { + checkPreconditions(); + + if (restconfInstance == null) { + return InstanceIdentifierContext.ofLocalRoot(globalSchema); + } + + final List<String> pathArgs = urlPathArgsDecode(SLASH_SPLITTER.split(restconfInstance)); + omitFirstAndLastEmptyString(pathArgs); + if (pathArgs.isEmpty()) { + return null; + } + + final String first = pathArgs.iterator().next(); + final String startModule = toModuleName(first); + if (startModule == null) { + throw new RestconfDocumentedException("First node in URI has to be in format \"moduleName:nodeName\"", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final Collection<? extends Module> latestModule = globalSchema.findModules(startModule); + if (latestModule.isEmpty()) { + throw new RestconfDocumentedException("The module named '" + startModule + "' does not exist.", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + + final InstanceIdentifierContext iiWithSchemaNode = collectPathArguments(YangInstanceIdentifier.builder(), + new ArrayDeque<>(), pathArgs, latestModule.iterator().next(), null, toMountPointIdentifier); + + if (iiWithSchemaNode == null) { + throw new RestconfDocumentedException("URI has bad format", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + return iiWithSchemaNode; + } + + private static List<String> omitFirstAndLastEmptyString(final List<String> list) { + if (list.isEmpty()) { + return list; + } + + final String head = list.iterator().next(); + if (head.isEmpty()) { + list.remove(0); + } + + if (list.isEmpty()) { + return list; + } + + final String last = list.get(list.size() - 1); + if (last.isEmpty()) { + list.remove(list.size() - 1); + } + + return list; + } + + public Module findModuleByName(final String moduleName) { + checkPreconditions(); + checkArgument(moduleName != null && !moduleName.isEmpty()); + return globalSchema.findModules(moduleName).stream().findFirst().orElse(null); + } + + public static Module findModuleByName(final DOMMountPoint mountPoint, final String moduleName) { + checkArgument(moduleName != null && mountPoint != null); + + final EffectiveModelContext mountPointSchema = getModelContext(mountPoint); + return mountPointSchema == null ? null + : mountPointSchema.findModules(moduleName).stream().findFirst().orElse(null); + } + + public Module findModuleByNamespace(final XMLNamespace namespace) { + checkPreconditions(); + checkArgument(namespace != null); + return globalSchema.findModules(namespace).stream().findFirst().orElse(null); + } + + public static Module findModuleByNamespace(final DOMMountPoint mountPoint, final XMLNamespace namespace) { + checkArgument(namespace != null && mountPoint != null); + + final EffectiveModelContext mountPointSchema = getModelContext(mountPoint); + return mountPointSchema == null ? null + : mountPointSchema.findModules(namespace).stream().findFirst().orElse(null); + } + + public Module findModuleByNameAndRevision(final String name, final Revision revision) { + checkPreconditions(); + checkArgument(name != null && revision != null); + + return globalSchema.findModule(name, revision).orElse(null); + } + + public Module findModuleByNameAndRevision(final DOMMountPoint mountPoint, final String name, + final Revision revision) { + checkPreconditions(); + checkArgument(name != null && revision != null && mountPoint != null); + + final EffectiveModelContext schemaContext = getModelContext(mountPoint); + return schemaContext == null ? null : schemaContext.findModule(name, revision).orElse(null); + } + + public DataNodeContainer getDataNodeContainerFor(final YangInstanceIdentifier path) { + checkPreconditions(); + + final Iterable<PathArgument> elements = path.getPathArguments(); + final PathArgument head = elements.iterator().next(); + final QName startQName = head.getNodeType(); + final Module initialModule = globalSchema.findModule(startQName.getModule()).orElse(null); + DataNodeContainer node = initialModule; + for (final PathArgument element : elements) { + final QName _nodeType = element.getNodeType(); + final DataSchemaNode potentialNode = childByQName(node, _nodeType); + if (potentialNode == null || !isListOrContainer(potentialNode)) { + return null; + } + node = (DataNodeContainer) potentialNode; + } + + return node; + } + + public String toFullRestconfIdentifier(final YangInstanceIdentifier path, final DOMMountPoint mount) { + checkPreconditions(); + + final Iterable<PathArgument> elements = path.getPathArguments(); + final StringBuilder builder = new StringBuilder(); + final PathArgument head = elements.iterator().next(); + final QName startQName = head.getNodeType(); + final EffectiveModelContext schemaContext; + if (mount != null) { + schemaContext = getModelContext(mount); + } else { + schemaContext = globalSchema; + } + final Module initialModule = schemaContext.findModule(startQName.getModule()).orElse(null); + DataNodeContainer node = initialModule; + for (final PathArgument element : elements) { + if (!(element instanceof AugmentationIdentifier)) { + final QName _nodeType = element.getNodeType(); + final DataSchemaNode potentialNode = childByQName(node, _nodeType); + if ((!(element instanceof NodeIdentifier) || !(potentialNode instanceof ListSchemaNode)) + && !(potentialNode instanceof ChoiceSchemaNode)) { + builder.append(convertToRestconfIdentifier(element, potentialNode, mount)); + if (potentialNode instanceof DataNodeContainer) { + node = (DataNodeContainer) potentialNode; + } + } + } + } + + return builder.toString(); + } + + public String findModuleNameByNamespace(final XMLNamespace namespace) { + checkPreconditions(); + + final Module module = findModuleByNamespace(namespace); + return module == null ? null : module.getName(); + } + + public static String findModuleNameByNamespace(final DOMMountPoint mountPoint, final XMLNamespace namespace) { + final Module module = findModuleByNamespace(mountPoint, namespace); + return module == null ? null : module.getName(); + } + + public XMLNamespace findNamespaceByModuleName(final String moduleName) { + final Module module = findModuleByName(moduleName); + return module == null ? null : module.getNamespace(); + } + + public static XMLNamespace findNamespaceByModuleName(final DOMMountPoint mountPoint, final String moduleName) { + final Module module = findModuleByName(mountPoint, moduleName); + return module == null ? null : module.getNamespace(); + } + + public Collection<? extends Module> getAllModules(final DOMMountPoint mountPoint) { + checkPreconditions(); + + final EffectiveModelContext schemaContext = mountPoint == null ? null : getModelContext(mountPoint); + return schemaContext == null ? null : schemaContext.getModules(); + } + + public Collection<? extends Module> getAllModules() { + checkPreconditions(); + return globalSchema.getModules(); + } + + private static String toRestconfIdentifier(final EffectiveModelContext context, final QName qname) { + final Module schema = context.findModule(qname.getModule()).orElse(null); + return schema == null ? null : schema.getName() + ':' + qname.getLocalName(); + } + + public String toRestconfIdentifier(final QName qname, final DOMMountPoint mountPoint) { + return mountPoint != null ? toRestconfIdentifier(getModelContext(mountPoint), qname) + : toRestconfIdentifier(qname); + } + + public String toRestconfIdentifier(final QName qname) { + checkPreconditions(); + + return toRestconfIdentifier(globalSchema, qname); + } + + public static String toRestconfIdentifier(final DOMMountPoint mountPoint, final QName qname) { + return mountPoint == null ? null : toRestconfIdentifier(getModelContext(mountPoint), qname); + } + + public Module getRestconfModule() { + return findModuleByNameAndRevision(Draft02.RestConfModule.NAME, Revision.of(Draft02.RestConfModule.REVISION)); + } + + public Entry<SchemaInferenceStack, ContainerSchemaNode> getRestconfModuleErrorsSchemaNode() { + checkPreconditions(); + + final var schema = globalSchema; + final var namespace = QNameModule.create(XMLNamespace.of(Draft02.RestConfModule.NAMESPACE), + Revision.of(Draft02.RestConfModule.REVISION)); + if (schema.findModule(namespace).isEmpty()) { + return null; + } + + final var stack = SchemaInferenceStack.of(globalSchema); + stack.enterGrouping(RestConfModule.ERRORS_QNAME); + final var stmt = stack.enterSchemaTree(RestConfModule.ERRORS_QNAME); + verify(stmt instanceof ContainerSchemaNode, "Unexpected statement %s", stmt); + return Map.entry(stack, (ContainerSchemaNode) stmt); + } + + public DataSchemaNode getRestconfModuleRestConfSchemaNode(final Module inRestconfModule, + final String schemaNodeName) { + Module restconfModule = inRestconfModule; + if (restconfModule == null) { + restconfModule = getRestconfModule(); + } + + if (restconfModule == null) { + return null; + } + + final Collection<? extends GroupingDefinition> groupings = restconfModule.getGroupings(); + final Iterable<? extends GroupingDefinition> filteredGroups = Iterables.filter(groupings, + g -> RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE.equals(g.getQName().getLocalName())); + final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null); + + final var instanceDataChildrenByName = findInstanceDataChildrenByName(restconfGrouping, + RestConfModule.RESTCONF_CONTAINER_SCHEMA_NODE); + final DataSchemaNode restconfContainer = getFirst(instanceDataChildrenByName); + + if (RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE.equals(schemaNodeName)) { + final var instances = findInstanceDataChildrenByName( + (DataNodeContainer) restconfContainer, RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + return getFirst(instances); + } else if (RestConfModule.STREAM_LIST_SCHEMA_NODE.equals(schemaNodeName)) { + var instances = findInstanceDataChildrenByName( + (DataNodeContainer) restconfContainer, RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + final DataSchemaNode modules = getFirst(instances); + instances = findInstanceDataChildrenByName((DataNodeContainer) modules, + RestConfModule.STREAM_LIST_SCHEMA_NODE); + return getFirst(instances); + } else if (RestConfModule.MODULES_CONTAINER_SCHEMA_NODE.equals(schemaNodeName)) { + final var instances = findInstanceDataChildrenByName( + (DataNodeContainer) restconfContainer, RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + return getFirst(instances); + } else if (RestConfModule.MODULE_LIST_SCHEMA_NODE.equals(schemaNodeName)) { + var instances = findInstanceDataChildrenByName( + (DataNodeContainer) restconfContainer, RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + final DataSchemaNode modules = getFirst(instances); + instances = findInstanceDataChildrenByName((DataNodeContainer) modules, + RestConfModule.MODULE_LIST_SCHEMA_NODE); + return getFirst(instances); + } else if (RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE.equals(schemaNodeName)) { + final var instances = findInstanceDataChildrenByName( + (DataNodeContainer) restconfContainer, RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + return getFirst(instances); + } + + return null; + } + + public static @Nullable DataSchemaNode getFirst(final List<FoundChild> children) { + return children.isEmpty() ? null : children.get(0).child; + } + + private static DataSchemaNode childByQName(final ChoiceSchemaNode container, final QName name) { + for (final CaseSchemaNode caze : container.getCases()) { + final DataSchemaNode ret = childByQName(caze, name); + if (ret != null) { + return ret; + } + } + + return null; + } + + private static DataSchemaNode childByQName(final CaseSchemaNode container, final QName name) { + return container.dataChildByName(name); + } + + private static DataSchemaNode childByQName(final ContainerSchemaNode container, final QName name) { + return dataNodeChildByQName(container, name); + } + + private static DataSchemaNode childByQName(final ListSchemaNode container, final QName name) { + return dataNodeChildByQName(container, name); + } + + private static DataSchemaNode childByQName(final Module container, final QName name) { + return dataNodeChildByQName(container, name); + } + + private static DataSchemaNode childByQName(final DataSchemaNode container, final QName name) { + return null; + } + + + private static DataSchemaNode childByQName(final Object container, final QName name) { + if (container instanceof CaseSchemaNode) { + return childByQName((CaseSchemaNode) container, name); + } else if (container instanceof ChoiceSchemaNode) { + return childByQName((ChoiceSchemaNode) container, name); + } else if (container instanceof ContainerSchemaNode) { + return childByQName((ContainerSchemaNode) container, name); + } else if (container instanceof ListSchemaNode) { + return childByQName((ListSchemaNode) container, name); + } else if (container instanceof DataSchemaNode) { + return childByQName((DataSchemaNode) container, name); + } else if (container instanceof Module) { + return childByQName((Module) container, name); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays.asList(container, name).toString()); + } + } + + private static DataSchemaNode dataNodeChildByQName(final DataNodeContainer container, final QName name) { + final DataSchemaNode ret = container.dataChildByName(name); + if (ret == null) { + for (final DataSchemaNode node : container.getChildNodes()) { + if (node instanceof ChoiceSchemaNode) { + final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) node; + final DataSchemaNode childByQName = childByQName(choiceNode, name); + if (childByQName != null) { + return childByQName; + } + } + } + } + return ret; + } + + private String toUriString(final Object object, final LeafSchemaNode leafNode, final DOMMountPoint mount) + throws UnsupportedEncodingException { + final IllegalArgumentCodec<Object, Object> codec = RestCodec.from(leafNode.getType(), mount, this); + return object == null ? "" : URLEncoder.encode(codec.serialize(object).toString(), StandardCharsets.UTF_8); + } + + @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification = "Unrecognised NullableDecl") + private InstanceIdentifierContext collectPathArguments(final InstanceIdentifierBuilder builder, + final Deque<QName> schemaPath, final List<String> strings, final DataNodeContainer parentNode, + final DOMMountPoint mountPoint, final boolean returnJustMountPoint) { + requireNonNull(strings); + + if (parentNode == null) { + return null; + } + + if (strings.isEmpty()) { + return createContext(builder.build(), (DataSchemaNode) parentNode, mountPoint, + mountPoint != null ? getModelContext(mountPoint) : globalSchema); + } + + final String head = strings.iterator().next(); + + if (head.isEmpty()) { + final List<String> remaining = strings.subList(1, strings.size()); + return collectPathArguments(builder, schemaPath, remaining, parentNode, mountPoint, returnJustMountPoint); + } + + final String nodeName = toNodeName(head); + final String moduleName = toModuleName(head); + + DataSchemaNode targetNode = null; + if (!Strings.isNullOrEmpty(moduleName)) { + if (MOUNT_MODULE.equals(moduleName) && MOUNT_NODE.equals(nodeName)) { + if (mountPoint != null) { + throw new RestconfDocumentedException("Restconf supports just one mount point in URI.", + ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED); + } + + if (mountService == null) { + throw new RestconfDocumentedException( + "MountService was not found. Finding behind mount points does not work.", + ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED); + } + + final YangInstanceIdentifier partialPath = dataNormalizer.toNormalized(builder.build()).getKey(); + final Optional<DOMMountPoint> mountOpt = mountService.getMountPoint(partialPath); + if (mountOpt.isEmpty()) { + LOG.debug("Instance identifier to missing mount point: {}", partialPath); + throw new RestconfDocumentedException("Mount point does not exist.", ErrorType.PROTOCOL, + ErrorTag.DATA_MISSING); + } + final DOMMountPoint mount = mountOpt.get(); + + final EffectiveModelContext mountPointSchema = getModelContext(mount); + if (mountPointSchema == null) { + throw new RestconfDocumentedException("Mount point does not contain any schema with modules.", + ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT); + } + + if (returnJustMountPoint || strings.size() == 1) { + return InstanceIdentifierContext.ofMountPointRoot(mount, mountPointSchema); + } + + final String moduleNameBehindMountPoint = toModuleName(strings.get(1)); + if (moduleNameBehindMountPoint == null) { + throw new RestconfDocumentedException( + "First node after mount point in URI has to be in format \"moduleName:nodeName\"", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final Iterator<? extends Module> it = mountPointSchema.findModules(moduleNameBehindMountPoint) + .iterator(); + if (!it.hasNext()) { + throw new RestconfDocumentedException("\"" + moduleNameBehindMountPoint + + "\" module does not exist in mount point.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + + final List<String> subList = strings.subList(1, strings.size()); + return collectPathArguments(YangInstanceIdentifier.builder(), new ArrayDeque<>(), subList, it.next(), + mount, returnJustMountPoint); + } + + Module module = null; + if (mountPoint == null) { + checkPreconditions(); + module = globalSchema.findModules(moduleName).stream().findFirst().orElse(null); + if (module == null) { + throw new RestconfDocumentedException("\"" + moduleName + "\" module does not exist.", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + } else { + final EffectiveModelContext schemaContext = getModelContext(mountPoint); + if (schemaContext != null) { + module = schemaContext.findModules(moduleName).stream().findFirst().orElse(null); + } else { + module = null; + } + if (module == null) { + throw new RestconfDocumentedException("\"" + moduleName + + "\" module does not exist in mount point.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + } + + final var found = findInstanceDataChildByNameAndNamespace(parentNode, nodeName, module.getNamespace()); + if (found == null) { + if (parentNode instanceof Module) { + final RpcDefinition rpc; + if (mountPoint == null) { + rpc = getRpcDefinition(head, module.getRevision()); + } else { + rpc = getRpcDefinition(module, toNodeName(head)); + } + if (rpc != null) { + final var ctx = mountPoint == null ? globalSchema : getModelContext(mountPoint); + return InstanceIdentifierContext.ofRpcInput(ctx, rpc, mountPoint); + } + } + throw new RestconfDocumentedException("URI has bad format. Possible reasons:\n" + " 1. \"" + head + + "\" was not found in parent data node.\n" + " 2. \"" + head + + "\" is behind mount point. Then it should be in format \"/" + MOUNT + "/" + head + "\".", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + targetNode = found.child; + schemaPath.addAll(found.intermediate); + schemaPath.add(targetNode.getQName()); + } else { + final var potentialSchemaNodes = findInstanceDataChildrenByName(parentNode, nodeName); + if (potentialSchemaNodes.size() > 1) { + final StringBuilder strBuilder = new StringBuilder(); + for (var potentialNodeSchema : potentialSchemaNodes) { + strBuilder.append(" ").append(potentialNodeSchema.child.getQName().getNamespace()).append("\n"); + } + + throw new RestconfDocumentedException( + "URI has bad format. Node \"" + + nodeName + "\" is added as augment from more than one module. " + + "Therefore the node must have module name " + + "and it has to be in format \"moduleName:nodeName\"." + + "\nThe node is added as augment from modules with namespaces:\n" + + strBuilder.toString(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + if (potentialSchemaNodes.isEmpty()) { + throw new RestconfDocumentedException("\"" + nodeName + "\" in URI was not found in parent data node", + ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + + final var found = potentialSchemaNodes.get(0); + targetNode = found.child; + schemaPath.addAll(found.intermediate); + schemaPath.add(targetNode.getQName()); + } + + if (!isListOrContainer(targetNode)) { + throw new RestconfDocumentedException("URI has bad format. Node \"" + head + + "\" must be Container or List yang type.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + int consumed = 1; + if (targetNode instanceof ListSchemaNode) { + final ListSchemaNode listNode = (ListSchemaNode) targetNode; + final int keysSize = listNode.getKeyDefinition().size(); + if (strings.size() - consumed < keysSize) { + throw new RestconfDocumentedException("Missing key for list \"" + listNode.getQName().getLocalName() + + "\".", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); + } + + final List<String> uriKeyValues = strings.subList(consumed, consumed + keysSize); + final HashMap<QName, Object> keyValues = new HashMap<>(); + int index = 0; + for (final QName key : listNode.getKeyDefinition()) { + final String uriKeyValue = uriKeyValues.get(index); + if (uriKeyValue.equals(NULL_VALUE)) { + throw new RestconfDocumentedException("URI has bad format. List \"" + + listNode.getQName().getLocalName() + "\" cannot contain \"null\" value as a key.", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final var keyChild = listNode.getDataChildByName(key); + schemaPath.addLast(keyChild.getQName()); + addKeyValue(keyValues, schemaPath, keyChild, uriKeyValue, mountPoint); + schemaPath.removeLast(); + index++; + } + + consumed = consumed + index; + builder.nodeWithKey(targetNode.getQName(), keyValues); + } else { + builder.node(targetNode.getQName()); + } + + if (targetNode instanceof DataNodeContainer) { + final List<String> remaining = strings.subList(consumed, strings.size()); + return collectPathArguments(builder, schemaPath, remaining, (DataNodeContainer) targetNode, mountPoint, + returnJustMountPoint); + } + + return createContext(builder.build(), targetNode, mountPoint, + mountPoint != null ? getModelContext(mountPoint) : globalSchema); + } + + private static InstanceIdentifierContext createContext(final YangInstanceIdentifier instance, + final DataSchemaNode dataSchemaNode, final DOMMountPoint mountPoint, + final EffectiveModelContext schemaContext) { + final var normalized = new DataNormalizer(schemaContext).toNormalized(instance); + return InstanceIdentifierContext.ofPath(normalized.getValue(), dataSchemaNode, normalized.getKey(), mountPoint); + } + + public static @Nullable FoundChild findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, + final String name, final XMLNamespace namespace) { + requireNonNull(namespace); + + for (var node : findInstanceDataChildrenByName(container, name)) { + if (namespace.equals(node.child.getQName().getNamespace())) { + return node; + } + } + return null; + } + + public static List<FoundChild> findInstanceDataChildrenByName(final DataNodeContainer container, + final String name) { + final List<FoundChild> instantiatedDataNodeContainers = new ArrayList<>(); + collectInstanceDataNodeContainers(instantiatedDataNodeContainers, requireNonNull(container), + requireNonNull(name), List.of()); + return instantiatedDataNodeContainers; + } + + private static void collectInstanceDataNodeContainers(final List<FoundChild> potentialSchemaNodes, + final DataNodeContainer container, final String name, final List<QName> intermediate) { + // We perform two iterations to retain breadth-first ordering + for (var child : container.getChildNodes()) { + if (name.equals(child.getQName().getLocalName()) && isInstantiatedDataSchema(child)) { + potentialSchemaNodes.add(new FoundChild(child, intermediate)); + } + } + + for (var child : container.getChildNodes()) { + if (child instanceof ChoiceSchemaNode) { + for (var caze : ((ChoiceSchemaNode) child).getCases()) { + collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name, + ImmutableList.<QName>builderWithExpectedSize(intermediate.size() + 2) + .addAll(intermediate).add(child.getQName()).add(caze.getQName()) + .build()); + } + } + } + } + + public static boolean isInstantiatedDataSchema(final DataSchemaNode node) { + return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode + || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode + || node instanceof AnyxmlSchemaNode; + } + + private void addKeyValue(final HashMap<QName, Object> map, final Deque<QName> schemaPath, final DataSchemaNode node, + final String uriValue, final DOMMountPoint mountPoint) { + checkArgument(node instanceof LeafSchemaNode); + + final EffectiveModelContext schemaContext = mountPoint == null ? globalSchema : getModelContext(mountPoint); + final String urlDecoded = urlPathArgDecode(requireNonNull(uriValue)); + TypeDefinition<?> typedef = ((LeafSchemaNode) node).getType(); + final TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(typedef); + if (baseType instanceof LeafrefTypeDefinition) { + final var stack = SchemaInferenceStack.of(schemaContext); + schemaPath.forEach(stack::enterSchemaTree); + typedef = stack.resolveLeafref((LeafrefTypeDefinition) baseType); + } + final IllegalArgumentCodec<Object, Object> codec = RestCodec.from(typedef, mountPoint, this); + Object decoded = codec.deserialize(urlDecoded); + String additionalInfo = ""; + if (decoded == null) { + if (typedef instanceof IdentityrefTypeDefinition) { + decoded = toQName(schemaContext, urlDecoded); + additionalInfo = + "For key which is of type identityref it should be in format module_name:identity_name."; + } + } + + if (decoded == null) { + throw new RestconfDocumentedException(uriValue + " from URI can't be resolved. " + additionalInfo, + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + map.put(node.getQName(), decoded); + } + + private static String toModuleName(final String str) { + final int idx = str.indexOf(':'); + if (idx == -1) { + return null; + } + + // Make sure there is only one occurrence + if (str.indexOf(':', idx + 1) != -1) { + return null; + } + + return str.substring(0, idx); + } + + private static String toNodeName(final String str) { + final int idx = str.indexOf(':'); + if (idx == -1) { + return str; + } + + // Make sure there is only one occurrence + if (str.indexOf(':', idx + 1) != -1) { + return str; + } + + return str.substring(idx + 1); + } + + private QName toQName(final EffectiveModelContext schemaContext, final String name, + final Optional<Revision> revisionDate) { + checkPreconditions(); + final String module = toModuleName(name); + final String node = toNodeName(name); + final Module m = schemaContext.findModule(module, revisionDate).orElse(null); + return m == null ? null : QName.create(m.getQNameModule(), node); + } + + private QName toQName(final EffectiveModelContext schemaContext, final String name) { + checkPreconditions(); + final String module = toModuleName(name); + final String node = toNodeName(name); + final Collection<? extends Module> modules = schemaContext.findModules(module); + return modules.isEmpty() ? null : QName.create(modules.iterator().next().getQNameModule(), node); + } + + private static boolean isListOrContainer(final DataSchemaNode node) { + return node instanceof ListSchemaNode || node instanceof ContainerSchemaNode; + } + + public RpcDefinition getRpcDefinition(final String name, final Optional<Revision> revisionDate) { + final QName validName = toQName(globalSchema, name, revisionDate); + return validName == null ? null : qnameToRpc.get().get(validName); + } + + public RpcDefinition getRpcDefinition(final String name) { + final QName validName = toQName(globalSchema, name); + return validName == null ? null : qnameToRpc.get().get(validName); + } + + private static RpcDefinition getRpcDefinition(final Module module, final String rpcName) { + final QName rpcQName = QName.create(module.getQNameModule(), rpcName); + for (final RpcDefinition rpcDefinition : module.getRpcs()) { + if (rpcQName.equals(rpcDefinition.getQName())) { + return rpcDefinition; + } + } + return null; + } + + @Override + public void onModelContextUpdated(final EffectiveModelContext context) { + if (context != null) { + final Collection<? extends RpcDefinition> defs = context.getOperations(); + final Map<QName, RpcDefinition> newMap = new HashMap<>(defs.size()); + + for (final RpcDefinition operation : defs) { + newMap.put(operation.getQName(), operation); + } + + // FIXME: still not completely atomic + qnameToRpc.set(ImmutableMap.copyOf(newMap)); + setGlobalSchema(context); + } + } + + private static List<String> urlPathArgsDecode(final Iterable<String> strings) { + final List<String> decodedPathArgs = new ArrayList<>(); + for (final String pathArg : strings) { + final String _decode = URLDecoder.decode(pathArg, StandardCharsets.UTF_8); + decodedPathArgs.add(_decode); + } + return decodedPathArgs; + } + + static String urlPathArgDecode(final String pathArg) { + if (pathArg == null) { + return null; + } + return URLDecoder.decode(pathArg, StandardCharsets.UTF_8); + } + + private String convertToRestconfIdentifier(final PathArgument argument, final DataSchemaNode node, + final DOMMountPoint mount) { + if (argument instanceof NodeIdentifier) { + return convertToRestconfIdentifier((NodeIdentifier) argument, mount); + } else if (argument instanceof NodeIdentifierWithPredicates && node instanceof ListSchemaNode) { + return convertToRestconfIdentifierWithPredicates((NodeIdentifierWithPredicates) argument, + (ListSchemaNode) node, mount); + } else if (argument != null && node != null) { + throw new IllegalArgumentException("Conversion of generic path argument is not supported"); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(argument, node)); + } + } + + private String convertToRestconfIdentifier(final NodeIdentifier argument, final DOMMountPoint node) { + return "/" + toRestconfIdentifier(argument.getNodeType(), node); + } + + private String convertToRestconfIdentifierWithPredicates(final NodeIdentifierWithPredicates argument, + final ListSchemaNode node, final DOMMountPoint mount) { + final QName nodeType = argument.getNodeType(); + final String nodeIdentifier = toRestconfIdentifier(nodeType, mount); + + final StringBuilder builder = new StringBuilder().append('/').append(nodeIdentifier).append('/'); + + final List<QName> keyDefinition = node.getKeyDefinition(); + boolean hasElements = false; + for (final QName key : keyDefinition) { + for (final DataSchemaNode listChild : node.getChildNodes()) { + if (listChild.getQName().equals(key)) { + if (!hasElements) { + hasElements = true; + } else { + builder.append('/'); + } + + checkState(listChild instanceof LeafSchemaNode, + "List key has to consist of leaves, not %s", listChild); + + final Object value = argument.getValue(key); + try { + builder.append(toUriString(value, (LeafSchemaNode)listChild, mount)); + } catch (final UnsupportedEncodingException e) { + LOG.error("Error parsing URI: {}", value, e); + return null; + } + break; + } + } + } + + return builder.toString(); + } + + public YangInstanceIdentifier toXpathRepresentation(final YangInstanceIdentifier instanceIdentifier) { + if (dataNormalizer == null) { + throw new RestconfDocumentedException("Data normalizer isn't set. Normalization isn't possible"); + } + + try { + return dataNormalizer.toLegacy(instanceIdentifier); + } catch (final DataNormalizationException e) { + throw new RestconfDocumentedException("Data normalizer failed. Normalization isn't possible", e); + } + } + + public boolean isNodeMixin(final YangInstanceIdentifier path) { + final DataNormalizationOperation<?> operation; + try { + operation = dataNormalizer.getOperation(path); + } catch (final DataNormalizationException e) { + throw new RestconfDocumentedException("Data normalizer failed. Normalization isn't possible", e); + } + return operation.isMixin(); + } + + private static EffectiveModelContext getModelContext(final DOMMountPoint mountPoint) { + return mountPoint.getService(DOMSchemaService.class) + .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) + .orElse(null); + } + + public static final class FoundChild { + // Intermediate schema tree children, usually empty + public final @NonNull List<QName> intermediate; + public final @NonNull DataSchemaNode child; + + private FoundChild(final DataSchemaNode child, final List<QName> intermediate) { + this.child = requireNonNull(child); + this.intermediate = requireNonNull(intermediate); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationException.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationException.java new file mode 100644 index 0000000..b9e7214 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationException.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +final class DataNormalizationException extends Exception { + private static final long serialVersionUID = 1L; + + DataNormalizationException(final String message) { + super(message); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationOperation.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationOperation.java new file mode 100644 index 0000000..eb41c78 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizationOperation.java @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static com.google.common.base.Verify.verifyNotNull; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.yang.common.Empty; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; +import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +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.util.EffectiveAugmentationSchema; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> { + private final T identifier; + + DataNormalizationOperation(final T identifier) { + this.identifier = identifier; + } + + static DataNormalizationOperation<?> from(final EffectiveModelContext ctx) { + return new ContainerNormalization(ctx); + } + + @Override + public T getIdentifier() { + return identifier; + } + + boolean isMixin() { + return false; + } + + Set<QName> getQNameIdentifiers() { + return ImmutableSet.of(identifier.getNodeType()); + } + + abstract DataNormalizationOperation<?> getChild(PathArgument child) throws DataNormalizationException; + + abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException; + + abstract DataNormalizationOperation<?> enterChild(QName child, SchemaInferenceStack stack) + throws DataNormalizationException; + + abstract DataNormalizationOperation<?> enterChild(PathArgument child, SchemaInferenceStack stack) + throws DataNormalizationException; + + void pushToStack(final SchemaInferenceStack stack) { + // Accurate for most subclasses + stack.enterSchemaTree(getIdentifier().getNodeType()); + } + + private abstract static class SimpleTypeNormalization<T extends PathArgument> + extends DataNormalizationOperation<T> { + SimpleTypeNormalization(final T identifier) { + super(identifier); + } + + @Override + final DataNormalizationOperation<?> getChild(final PathArgument child) { + return null; + } + + @Override + final DataNormalizationOperation<?> getChild(final QName child) { + return null; + } + + @Override + final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) { + return null; + } + + @Override + final DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) { + return null; + } + } + + private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> { + LeafNormalization(final LeafSchemaNode potential) { + super(new NodeIdentifier(potential.getQName())); + } + } + + private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> { + LeafListEntryNormalization(final LeafListSchemaNode potential) { + super(new NodeWithValue<>(potential.getQName(), Empty.value())); + } + + @Override + protected void pushToStack(final SchemaInferenceStack stack) { + // No-op + } + } + + private abstract static class DataContainerNormalizationOperation<T extends PathArgument> + extends DataNormalizationOperation<T> { + private final DataNodeContainer schema; + private final Map<QName, DataNormalizationOperation<?>> byQName = new ConcurrentHashMap<>(); + private final Map<PathArgument, DataNormalizationOperation<?>> byArg = new ConcurrentHashMap<>(); + + DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) { + super(identifier); + this.schema = schema; + } + + @Override + DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException { + DataNormalizationOperation<?> potential = byArg.get(child); + if (potential != null) { + return potential; + } + potential = fromLocalSchema(child); + return register(potential); + } + + @Override + DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException { + DataNormalizationOperation<?> potential = byQName.get(child); + if (potential != null) { + return potential; + } + potential = fromLocalSchemaAndQName(schema, child); + return register(potential); + } + + @Override + final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) + throws DataNormalizationException { + return pushToStack(getChild(child), stack); + } + + @Override + final DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) + throws DataNormalizationException { + return pushToStack(getChild(child), stack); + } + + private static DataNormalizationOperation<?> pushToStack(final DataNormalizationOperation<?> child, + final SchemaInferenceStack stack) { + if (child != null) { + child.pushToStack(stack); + } + return child; + } + + private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child) + throws DataNormalizationException { + if (child instanceof AugmentationIdentifier) { + return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames() + .iterator().next()); + } + return fromSchemaAndQNameChecked(schema, child.getNodeType()); + } + + DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, + final QName child) throws DataNormalizationException { + return fromSchemaAndQNameChecked(schema2, child); + } + + private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) { + if (potential != null) { + byArg.put(potential.getIdentifier(), potential); + for (final QName qname : potential.getQNameIdentifiers()) { + byQName.put(qname, potential); + } + } + return potential; + } + + private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, + final QName child) throws DataNormalizationException { + + final DataSchemaNode result = findChildSchemaNode(schema, child); + if (result == null) { + throw new DataNormalizationException(String.format( + "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, + schema,schema.getChildNodes())); + } + + // We try to look up if this node was added by augmentation + if (schema instanceof DataSchemaNode && result.isAugmenting()) { + return fromAugmentation(schema, (AugmentationTarget) schema, result); + } + return fromDataSchemaNode(result); + } + } + + private static final class ListItemNormalization extends + DataContainerNormalizationOperation<NodeIdentifierWithPredicates> { + ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) { + super(identifier, schema); + } + + @Override + protected void pushToStack(final SchemaInferenceStack stack) { + // No-op + } + } + + private static final class UnkeyedListItemNormalization + extends DataContainerNormalizationOperation<NodeIdentifier> { + UnkeyedListItemNormalization(final ListSchemaNode schema) { + super(new NodeIdentifier(schema.getQName()), schema); + } + + @Override + protected void pushToStack(final SchemaInferenceStack stack) { + // No-op + } + } + + private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> { + ContainerNormalization(final ContainerLike schema) { + super(new NodeIdentifier(schema.getQName()), schema); + } + } + + private abstract static class MixinNormalizationOp<T extends PathArgument> extends DataNormalizationOperation<T> { + MixinNormalizationOp(final T identifier) { + super(identifier); + } + + @Override + final boolean isMixin() { + return true; + } + } + + private abstract static class ListLikeNormalizationOp<T extends PathArgument> extends MixinNormalizationOp<T> { + ListLikeNormalizationOp(final T identifier) { + super(identifier); + } + + @Override + protected final DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) + throws DataNormalizationException { + // Stack is already pointing to the corresponding statement, now we are just working with the child + return getChild(child); + } + + @Override + protected final DataNormalizationOperation<?> enterChild(final PathArgument child, + final SchemaInferenceStack stack) throws DataNormalizationException { + return getChild(child); + } + } + + private static final class LeafListMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> { + private final DataNormalizationOperation<?> innerOp; + + LeafListMixinNormalization(final LeafListSchemaNode potential) { + super(new NodeIdentifier(potential.getQName())); + innerOp = new LeafListEntryNormalization(potential); + } + + @Override + DataNormalizationOperation<?> getChild(final PathArgument child) { + if (child instanceof NodeWithValue) { + return innerOp; + } + return null; + } + + @Override + DataNormalizationOperation<?> getChild(final QName child) { + if (getIdentifier().getNodeType().equals(child)) { + return innerOp; + } + return null; + } + } + + private static final class AugmentationNormalization + extends DataContainerNormalizationOperation<AugmentationIdentifier> { + + AugmentationNormalization(final AugmentationSchemaNode augmentation, final DataNodeContainer schema) { + super(DataSchemaContextNode.augmentationIdentifierFrom(augmentation), + new EffectiveAugmentationSchema(augmentation, schema)); + } + + @Override + boolean isMixin() { + return true; + } + + @Override + DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child) { + final DataSchemaNode result = findChildSchemaNode(schema, child); + if (result == null) { + return null; + } + + // We try to look up if this node was added by augmentation + if (schema instanceof DataSchemaNode && result.isAugmenting()) { + return fromAugmentation(schema, (AugmentationTarget) schema, result); + } + return fromDataSchemaNode(result); + } + + @Override + Set<QName> getQNameIdentifiers() { + return getIdentifier().getPossibleChildNames(); + } + + @Override + void pushToStack(final SchemaInferenceStack stack) { + // No-op + } + } + + private static final class MapMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> { + private final ListItemNormalization innerNode; + + MapMixinNormalization(final ListSchemaNode list) { + super(new NodeIdentifier(list.getQName())); + innerNode = new ListItemNormalization(NodeIdentifierWithPredicates.of(list.getQName()), list); + } + + @Override + DataNormalizationOperation<?> getChild(final PathArgument child) { + if (child.getNodeType().equals(getIdentifier().getNodeType())) { + return innerNode; + } + return null; + } + + @Override + DataNormalizationOperation<?> getChild(final QName child) { + if (getIdentifier().getNodeType().equals(child)) { + return innerNode; + } + return null; + } + } + + private static final class UnkeyedListMixinNormalization extends ListLikeNormalizationOp<NodeIdentifier> { + private final UnkeyedListItemNormalization innerNode; + + UnkeyedListMixinNormalization(final ListSchemaNode list) { + super(new NodeIdentifier(list.getQName())); + innerNode = new UnkeyedListItemNormalization(list); + } + + @Override + DataNormalizationOperation<?> getChild(final PathArgument child) { + if (child.getNodeType().equals(getIdentifier().getNodeType())) { + return innerNode; + } + return null; + } + + @Override + DataNormalizationOperation<?> getChild(final QName child) { + if (getIdentifier().getNodeType().equals(child)) { + return innerNode; + } + return null; + } + } + + private static final class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> { + private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName; + private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg; + private final ImmutableMap<DataNormalizationOperation<?>, QName> childToCase; + + ChoiceNodeNormalization(final ChoiceSchemaNode schema) { + super(new NodeIdentifier(schema.getQName())); + ImmutableMap.Builder<DataNormalizationOperation<?>, QName> childToCaseBuilder = ImmutableMap.builder(); + final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder(); + final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = + ImmutableMap.builder(); + + for (final CaseSchemaNode caze : schema.getCases()) { + for (final DataSchemaNode cazeChild : caze.getChildNodes()) { + final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild); + byArgBuilder.put(childOp.getIdentifier(), childOp); + childToCaseBuilder.put(childOp, caze.getQName()); + for (final QName qname : childOp.getQNameIdentifiers()) { + byQNameBuilder.put(qname, childOp); + } + } + } + childToCase = childToCaseBuilder.build(); + byQName = byQNameBuilder.build(); + byArg = byArgBuilder.build(); + } + + @Override + DataNormalizationOperation<?> getChild(final PathArgument child) { + return byArg.get(child); + } + + @Override + DataNormalizationOperation<?> getChild(final QName child) { + return byQName.get(child); + } + + @Override + Set<QName> getQNameIdentifiers() { + return byQName.keySet(); + } + + @Override + DataNormalizationOperation<?> enterChild(final QName child, final SchemaInferenceStack stack) { + return pushToStack(getChild(child), stack); + } + + @Override + DataNormalizationOperation<?> enterChild(final PathArgument child, final SchemaInferenceStack stack) { + return pushToStack(getChild(child), stack); + } + + @Override + void pushToStack(final SchemaInferenceStack stack) { + stack.enterChoice(getIdentifier().getNodeType()); + } + + private DataNormalizationOperation<?> pushToStack(final DataNormalizationOperation<?> child, + final SchemaInferenceStack stack) { + if (child != null) { + final var caseName = verifyNotNull(childToCase.get(child), "No case statement for %s in %s", child, + this); + stack.enterSchemaTree(caseName); + child.pushToStack(stack); + } + return child; + } + } + + private static final class AnyxmlNormalization extends SimpleTypeNormalization<NodeIdentifier> { + AnyxmlNormalization(final AnyxmlSchemaNode schema) { + super(new NodeIdentifier(schema.getQName())); + } + } + + private static @Nullable DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) { + final DataSchemaNode potential = parent.dataChildByName(child); + return potential != null ? potential : findChoice(parent, child); + } + + private static @Nullable ChoiceSchemaNode findChoice(final DataNodeContainer parent, final QName child) { + for (final ChoiceSchemaNode choice : Iterables.filter(parent.getChildNodes(), ChoiceSchemaNode.class)) { + for (final CaseSchemaNode caze : choice.getCases()) { + if (findChildSchemaNode(caze, child) != null) { + return choice; + } + } + } + return null; + } + + /** + * Returns a DataNormalizationOperation for provided child node. + * + * <p> + * If supplied child is added by Augmentation this operation returns + * a DataNormalizationOperation for augmentation, + * otherwise returns a DataNormalizationOperation for child as + * call for {@link #fromDataSchemaNode(DataSchemaNode)}. + */ + private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent, + final AugmentationTarget parentAug, final DataSchemaNode child) { + for (final AugmentationSchemaNode aug : parentAug.getAvailableAugmentations()) { + if (aug.dataChildByName(child.getQName()) != null) { + return new AugmentationNormalization(aug, parent); + } + } + return fromDataSchemaNode(child); + } + + static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) { + if (potential instanceof ContainerSchemaNode) { + return new ContainerNormalization((ContainerSchemaNode) potential); + } else if (potential instanceof ListSchemaNode) { + return fromListSchemaNode((ListSchemaNode) potential); + } else if (potential instanceof LeafSchemaNode) { + return new LeafNormalization((LeafSchemaNode) potential); + } else if (potential instanceof ChoiceSchemaNode) { + return new ChoiceNodeNormalization((ChoiceSchemaNode) potential); + } else if (potential instanceof LeafListSchemaNode) { + return new LeafListMixinNormalization((LeafListSchemaNode) potential); + } else if (potential instanceof AnyxmlSchemaNode) { + return new AnyxmlNormalization((AnyxmlSchemaNode) potential); + } + return null; + } + + private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) { + if (potential.getKeyDefinition().isEmpty()) { + return new UnkeyedListMixinNormalization(potential); + } + return new MapMixinNormalization(potential); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java new file mode 100644 index 0000000..334a6c7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/DataNormalizer.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +class DataNormalizer { + private final DataNormalizationOperation<?> operation; + private final EffectiveModelContext context; + + DataNormalizer(final EffectiveModelContext ctx) { + context = requireNonNull(ctx); + operation = DataNormalizationOperation.from(ctx); + } + + Entry<YangInstanceIdentifier, SchemaInferenceStack> toNormalized(final YangInstanceIdentifier legacy) { + List<PathArgument> normalizedArgs = new ArrayList<>(); + + DataNormalizationOperation<?> currentOp = operation; + Iterator<PathArgument> arguments = legacy.getPathArguments().iterator(); + SchemaInferenceStack stack = SchemaInferenceStack.of(context); + + try { + while (arguments.hasNext()) { + PathArgument legacyArg = arguments.next(); + currentOp = currentOp.enterChild(legacyArg, stack); + checkArgument(currentOp != null, + "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s", + legacy, normalizedArgs); + while (currentOp.isMixin()) { + normalizedArgs.add(currentOp.getIdentifier()); + currentOp = currentOp.enterChild(legacyArg.getNodeType(), stack); + } + normalizedArgs.add(legacyArg); + } + } catch (DataNormalizationException e) { + throw new IllegalArgumentException("Failed to normalize path " + legacy, e); + } + + return Map.entry(YangInstanceIdentifier.create(normalizedArgs), stack); + } + + DataNormalizationOperation<?> getOperation(final YangInstanceIdentifier legacy) + throws DataNormalizationException { + DataNormalizationOperation<?> currentOp = operation; + + for (PathArgument pathArgument : legacy.getPathArguments()) { + currentOp = currentOp.getChild(pathArgument); + } + return currentOp; + } + + YangInstanceIdentifier toLegacy(final YangInstanceIdentifier normalized) throws DataNormalizationException { + ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder(); + DataNormalizationOperation<?> currentOp = operation; + for (PathArgument normalizedArg : normalized.getPathArguments()) { + currentOp = currentOp.getChild(normalizedArg); + if (!currentOp.isMixin()) { + legacyArgs.add(normalizedArg); + } + } + return YangInstanceIdentifier.create(legacyArgs.build()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/JSONRestconfServiceImpl.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/JSONRestconfServiceImpl.java new file mode 100644 index 0000000..296fd91 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/JSONRestconfServiceImpl.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static java.util.Objects.requireNonNull; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.PatchJsonBodyWriter; +import org.opendaylight.netconf.sal.restconf.api.JSONRestconfService; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.util.SimpleUriInfo; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.OperationFailedException; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implementation of the JSONRestconfService interface using the restconf Draft02 implementation. + * + * @author Thomas Pantelis + * @deprecated Replaced by {JSONRestconfServiceRfc8040Impl from restconf-nb-rfc8040 + */ +@Singleton +@Deprecated +public class JSONRestconfServiceImpl implements JSONRestconfService { + private static final Logger LOG = LoggerFactory.getLogger(JSONRestconfServiceImpl.class); + + private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0]; + + private final ControllerContext controllerContext; + private final RestconfService restconfService; + + @Inject + public JSONRestconfServiceImpl(final ControllerContext controllerContext, final RestconfImpl restconfService) { + this.controllerContext = controllerContext; + this.restconfService = restconfService; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public void put(final String uriPath, final String payload) throws OperationFailedException { + requireNonNull(payload, "payload can't be null"); + + LOG.debug("put: uriPath: {}, payload: {}", uriPath, payload); + + final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8)); + final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false, + controllerContext); + + LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier()); + LOG.debug("Parsed NormalizedNode: {}", context.getData()); + + try { + restconfService.updateConfigurationData(uriPath, context, new SimpleUriInfo(uriPath)); + } catch (final Exception e) { + propagateExceptionAs(uriPath, e, "PUT"); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public void post(final String uriPath, final String payload) throws OperationFailedException { + requireNonNull(payload, "payload can't be null"); + + LOG.debug("post: uriPath: {}, payload: {}", uriPath, payload); + + final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8)); + final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true, + controllerContext); + + LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier()); + LOG.debug("Parsed NormalizedNode: {}", context.getData()); + + try { + restconfService.createConfigurationData(uriPath, context, new SimpleUriInfo(uriPath)); + } catch (final Exception e) { + propagateExceptionAs(uriPath, e, "POST"); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public void delete(final String uriPath) throws OperationFailedException { + LOG.debug("delete: uriPath: {}", uriPath); + + try { + restconfService.deleteConfigurationData(uriPath); + } catch (final Exception e) { + propagateExceptionAs(uriPath, e, "DELETE"); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Optional<String> get(final String uriPath, final LogicalDatastoreType datastoreType) + throws OperationFailedException { + LOG.debug("get: uriPath: {}", uriPath); + + try { + NormalizedNodeContext readData; + final SimpleUriInfo uriInfo = new SimpleUriInfo(uriPath); + if (datastoreType == LogicalDatastoreType.CONFIGURATION) { + readData = restconfService.readConfigurationData(uriPath, uriInfo); + } else { + readData = restconfService.readOperationalData(uriPath, uriInfo); + } + + final Optional<String> result = Optional.of(toJson(readData)); + + LOG.debug("get returning: {}", result.get()); + + return result; + } catch (final Exception e) { + if (!isDataMissing(e)) { + propagateExceptionAs(uriPath, e, "GET"); + } + + LOG.debug("Data missing - returning absent"); + return Optional.empty(); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @SuppressFBWarnings(value = "NP_NULL_PARAM_DEREF", justification = "Unrecognised NullableDecl") + @Override + public Optional<String> invokeRpc(final String uriPath, final Optional<String> input) + throws OperationFailedException { + requireNonNull(uriPath, "uriPath can't be null"); + + final String actualInput = input.isPresent() ? input.get() : null; + + LOG.debug("invokeRpc: uriPath: {}, input: {}", uriPath, actualInput); + + String output = null; + try { + NormalizedNodeContext outputContext; + if (actualInput != null) { + final InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8)); + final NormalizedNodeContext inputContext = + JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true, controllerContext); + + LOG.debug("Parsed YangInstanceIdentifier: {}", inputContext.getInstanceIdentifierContext() + .getInstanceIdentifier()); + LOG.debug("Parsed NormalizedNode: {}", inputContext.getData()); + + outputContext = restconfService.invokeRpc(uriPath, inputContext, null); + } else { + outputContext = restconfService.invokeRpc(uriPath, null, null); + } + + if (outputContext.getData() != null) { + output = toJson(outputContext); + } + } catch (final RuntimeException | IOException e) { + propagateExceptionAs(uriPath, e, "RPC"); + } + + return Optional.ofNullable(output); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Optional<String> patch(final String uriPath, final String payload) + throws OperationFailedException { + + String output = null; + requireNonNull(payload, "payload can't be null"); + + LOG.debug("patch: uriPath: {}, payload: {}", uriPath, payload); + + final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8)); + + JsonToPatchBodyReader jsonToPatchBodyReader = new JsonToPatchBodyReader(controllerContext); + final PatchContext context = jsonToPatchBodyReader.readFrom(uriPath, entityStream); + + LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier()); + LOG.debug("Parsed NormalizedNode: {}", context.getData()); + + try { + PatchStatusContext patchStatusContext = restconfService + .patchConfigurationData(context, new SimpleUriInfo(uriPath)); + output = toJson(patchStatusContext); + } catch (final Exception e) { + propagateExceptionAs(uriPath, e, "PATCH"); + } + return Optional.ofNullable(output); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Optional<String> subscribeToStream(final String identifier, final MultivaluedMap<String, String> params) + throws OperationFailedException { + //Note: We use http://127.0.0.1 because the Uri parser requires something there though it does nothing + String uri = new StringBuilder("http://127.0.0.1:8081/restconf/streams/stream/").append(identifier).toString(); + MultivaluedMap queryParams = params != null ? params : new MultivaluedHashMap<String, String>(); + UriInfo uriInfo = new SimpleUriInfo(uri, queryParams); + + String jsonRes = null; + try { + NormalizedNodeContext res = restconfService.subscribeToStream(identifier, uriInfo); + jsonRes = toJson(res); + } catch (final Exception e) { + propagateExceptionAs(identifier, e, "RPC"); + } + + return Optional.ofNullable(jsonRes); + } + + private static String toJson(final PatchStatusContext patchStatusContext) throws IOException { + final PatchJsonBodyWriter writer = new PatchJsonBodyWriter(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + writer.writeTo(patchStatusContext, PatchStatusContext.class, null, EMPTY_ANNOTATIONS, + MediaType.APPLICATION_JSON_TYPE, null, outputStream); + return outputStream.toString(StandardCharsets.UTF_8); + } + + private static String toJson(final NormalizedNodeContext readData) throws IOException { + final NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + writer.writeTo(readData, NormalizedNodeContext.class, null, EMPTY_ANNOTATIONS, + MediaType.APPLICATION_JSON_TYPE, null, outputStream); + return outputStream.toString(StandardCharsets.UTF_8); + } + + private static boolean isDataMissing(final Exception exception) { + boolean dataMissing = false; + if (exception instanceof RestconfDocumentedException) { + final RestconfDocumentedException rde = (RestconfDocumentedException)exception; + if (!rde.getErrors().isEmpty()) { + if (rde.getErrors().get(0).getErrorTag() == ErrorTag.DATA_MISSING) { + dataMissing = true; + } + } + } + + return dataMissing; + } + + private static void propagateExceptionAs(final String uriPath, final Exception exception, final String operation) + throws OperationFailedException { + LOG.debug("Error for uriPath: {}", uriPath, exception); + + if (exception instanceof RestconfDocumentedException) { + throw new OperationFailedException(String.format( + "%s failed for URI %s", operation, uriPath), exception.getCause(), + toRpcErrors(((RestconfDocumentedException)exception).getErrors())); + } + + throw new OperationFailedException(String.format("%s failed for URI %s", operation, uriPath), exception); + } + + private static RpcError[] toRpcErrors(final List<RestconfError> from) { + final RpcError[] to = new RpcError[from.size()]; + int index = 0; + for (final RestconfError e: from) { + to[index++] = RpcResultBuilder.newError(e.getErrorType(), e.getErrorTag(), e.getErrorMessage()); + } + + return to; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/NormalizedDataPrunner.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/NormalizedDataPrunner.java new file mode 100644 index 0000000..975da60 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/NormalizedDataPrunner.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.AnyxmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; + +class NormalizedDataPrunner { + + public DataContainerChild pruneDataAtDepth(final DataContainerChild node, final Integer depth) { + if (depth == null) { + return node; + } + + if (node instanceof LeafNode || node instanceof LeafSetNode || node instanceof AnyxmlNode) { + return node; + } else if (node instanceof MixinNode) { + return processMixinNode(node, depth); + } else if (node instanceof DataContainerNode) { + return processContainerNode(node, depth); + } + throw new IllegalStateException("Unexpected Mixin node occured why pruning data to requested depth"); + } + + private DataContainerChild processMixinNode(final NormalizedNode node, final Integer depth) { + if (node instanceof AugmentationNode) { + return processAugmentationNode(node, depth); + } else if (node instanceof ChoiceNode) { + return processChoiceNode(node, depth); + } else if (node instanceof UserMapNode) { + return processOrderedMapNode(node, depth); + } else if (node instanceof MapNode) { + return processMapNode(node, depth); + } else if (node instanceof UnkeyedListNode) { + return processUnkeyedListNode(node, depth); + } + throw new IllegalStateException("Unexpected Mixin node occured why pruning data to requested depth"); + } + + private DataContainerChild processContainerNode(final NormalizedNode node, final Integer depth) { + final ContainerNode containerNode = (ContainerNode) node; + DataContainerNodeBuilder<NodeIdentifier, ContainerNode> newContainerBuilder = Builders.containerBuilder() + .withNodeIdentifier(containerNode.getIdentifier()); + if (depth > 1) { + processDataContainerChild((DataContainerNode) node, depth, newContainerBuilder); + } + return newContainerBuilder.build(); + } + + private DataContainerChild processChoiceNode(final NormalizedNode node, final Integer depth) { + final ChoiceNode choiceNode = (ChoiceNode) node; + DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> newChoiceBuilder = Builders.choiceBuilder() + .withNodeIdentifier(choiceNode.getIdentifier()); + + processDataContainerChild((DataContainerNode) node, depth, newChoiceBuilder); + + return newChoiceBuilder.build(); + } + + private DataContainerChild processAugmentationNode(final NormalizedNode node, final Integer depth) { + final AugmentationNode augmentationNode = (AugmentationNode) node; + DataContainerNodeBuilder<AugmentationIdentifier, ? extends DataContainerChild> newAugmentationBuilder = + Builders.augmentationBuilder().withNodeIdentifier(augmentationNode.getIdentifier()); + + processDataContainerChild((DataContainerNode) node, depth, newAugmentationBuilder); + + return newAugmentationBuilder.build(); + } + + private void processDataContainerChild(final DataContainerNode node, final Integer depth, + final DataContainerNodeBuilder<?, ?> newBuilder) { + for (DataContainerChild nodeValue : node.body()) { + newBuilder.withChild(pruneDataAtDepth(nodeValue, depth - 1)); + } + } + + private DataContainerChild processUnkeyedListNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> newUnkeyedListBuilder = Builders + .unkeyedListBuilder(); + if (depth > 1) { + for (UnkeyedListEntryNode oldUnkeyedListEntry : ((UnkeyedListNode) node).body()) { + DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> newUnkeyedListEntry = Builders + .unkeyedListEntryBuilder().withNodeIdentifier(oldUnkeyedListEntry.getIdentifier()); + for (DataContainerChild oldUnkeyedListEntryValue : oldUnkeyedListEntry.body()) { + newUnkeyedListEntry.withChild(pruneDataAtDepth(oldUnkeyedListEntryValue, depth - 1)); + } + newUnkeyedListBuilder.addChild(newUnkeyedListEntry.build()); + } + } + return newUnkeyedListBuilder.build(); + } + + private DataContainerChild processOrderedMapNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder<MapEntryNode, UserMapNode> newOrderedMapNodeBuilder = Builders.orderedMapBuilder(); + processMapEntries(node, depth, newOrderedMapNodeBuilder); + return newOrderedMapNodeBuilder.build(); + } + + private DataContainerChild processMapNode(final NormalizedNode node, final Integer depth) { + CollectionNodeBuilder<MapEntryNode, SystemMapNode> newMapNodeBuilder = Builders.mapBuilder(); + processMapEntries(node, depth, newMapNodeBuilder); + return newMapNodeBuilder.build(); + } + + private void processMapEntries(final NormalizedNode node, final Integer depth, + final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> newOrderedMapNodeBuilder) { + if (depth > 1) { + for (MapEntryNode oldMapEntryNode : ((MapNode) node).body()) { + DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> newMapEntryNodeBuilder = + Builders.mapEntryBuilder().withNodeIdentifier(oldMapEntryNode.getIdentifier()); + for (DataContainerChild mapEntryNodeValue : oldMapEntryNode.body()) { + newMapEntryNodeBuilder.withChild(pruneDataAtDepth(mapEntryNodeValue, depth - 1)); + } + newOrderedMapNodeBuilder.withChild(newMapEntryNodeBuilder.build()); + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PutResult.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PutResult.java new file mode 100644 index 0000000..87e33b9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PutResult.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import com.google.common.util.concurrent.FluentFuture; +import javax.ws.rs.core.Response.Status; +import org.opendaylight.mdsal.common.api.CommitInfo; + +/** + * Wrapper for status and future of PUT operation. + */ +public class PutResult { + private final Status status; + private final FluentFuture<? extends CommitInfo> future; + + /** + * Wrap status and future by constructor - make this immutable. + * + * @param status + * status of operations + * @param future + * result of submit of PUT operation + */ + public PutResult(final Status status, final FluentFuture<? extends CommitInfo> future) { + this.status = status; + this.future = future; + } + + /** + * Get status. + * + * @return {@link Status} result + */ + public Status getStatus() { + return this.status; + } + + /** + * Get future. + * + * @return {@link FluentFuture} result + */ + public FluentFuture<? extends CommitInfo> getFutureOfPutData() { + return this.future; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/QueryParametersParser.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/QueryParametersParser.java new file mode 100644 index 0000000..098c53b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/QueryParametersParser.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import com.google.common.base.Strings; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.netconf.sal.rest.impl.WriterParameters; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; + +public final class QueryParametersParser { + + private enum UriParameters { + PRETTY_PRINT("prettyPrint"), + DEPTH("depth"); + + private final String uriParameterName; + + UriParameters(final String uriParameterName) { + this.uriParameterName = uriParameterName; + } + + @Override + public String toString() { + return uriParameterName; + } + } + + private QueryParametersParser() { + + } + + public static WriterParameters parseWriterParameters(final UriInfo info) { + final WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder(); + if (info == null) { + return wpBuilder.build(); + } + + String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString()); + if (!Strings.isNullOrEmpty(param) && !"unbounded".equals(param)) { + try { + final int depth = Integer.parseInt(param); + if (depth < 1) { + throw new RestconfDocumentedException( + new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + "Invalid depth parameter: " + depth, null, + "The depth parameter must be an integer > 1 or \"unbounded\"")); + } + wpBuilder.setDepth(depth); + } catch (final NumberFormatException e) { + throw new RestconfDocumentedException(e, new RestconfError( + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + "Invalid depth parameter: " + e.getMessage(), null, + "The depth parameter must be an integer > 1 or \"unbounded\"")); + } + } + param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString()); + wpBuilder.setPrettyPrint("true".equals(param)); + return wpBuilder.build(); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestCodec.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestCodec.java new file mode 100644 index 0000000..5d7a840 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestCodec.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.netconf.sal.rest.impl.StringModuleInstanceIdentifierCodec; +import org.opendaylight.restconf.common.util.IdentityValuesDTO; +import org.opendaylight.restconf.common.util.IdentityValuesDTO.IdentityValue; +import org.opendaylight.restconf.common.util.IdentityValuesDTO.Predicate; +import org.opendaylight.restconf.common.util.RestUtil; +import org.opendaylight.yangtools.concepts.IllegalArgumentCodec; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec; +import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec; +import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class RestCodec { + + private static final Logger LOG = LoggerFactory.getLogger(RestCodec.class); + + private RestCodec() { + } + + // FIXME: IllegalArgumentCodec is not quite accurate + public static IllegalArgumentCodec<Object, Object> from(final TypeDefinition<?> typeDefinition, + final DOMMountPoint mountPoint, final ControllerContext controllerContext) { + return new ObjectCodec(typeDefinition, mountPoint, controllerContext); + } + + @SuppressWarnings("rawtypes") + public static final class ObjectCodec implements IllegalArgumentCodec<Object, Object> { + + private static final Logger LOG = LoggerFactory.getLogger(ObjectCodec.class); + + public static final IllegalArgumentCodec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl(); + + private final ControllerContext controllerContext; + private final IllegalArgumentCodec instanceIdentifier; + private final IllegalArgumentCodec identityrefCodec; + + private final TypeDefinition<?> type; + + private ObjectCodec(final TypeDefinition<?> typeDefinition, final DOMMountPoint mountPoint, + final ControllerContext controllerContext) { + this.controllerContext = controllerContext; + type = RestUtil.resolveBaseTypeFrom(typeDefinition); + if (type instanceof IdentityrefTypeDefinition) { + identityrefCodec = new IdentityrefCodecImpl(mountPoint, controllerContext); + } else { + identityrefCodec = null; + } + if (type instanceof InstanceIdentifierTypeDefinition) { + instanceIdentifier = new InstanceIdentifierCodecImpl(mountPoint, controllerContext); + } else { + instanceIdentifier = null; + } + } + + @SuppressWarnings("unchecked") + @Override + @SuppressFBWarnings(value = "NP_NONNULL_RETURN_VIOLATION", justification = "Legacy code") + public Object deserialize(final Object input) { + try { + if (type instanceof IdentityrefTypeDefinition) { + if (input instanceof IdentityValuesDTO) { + return identityrefCodec.deserialize(input); + } + if (LOG.isDebugEnabled()) { + LOG.debug( + "Value is not instance of IdentityrefTypeDefinition but is {}. " + + "Therefore NULL is used as translation of - {}", + input == null ? "null" : input.getClass(), String.valueOf(input)); + } + // FIXME: this should be a hard error + return null; + } else if (type instanceof InstanceIdentifierTypeDefinition) { + if (input instanceof IdentityValuesDTO) { + return instanceIdentifier.deserialize(input); + } else { + final StringModuleInstanceIdentifierCodec codec = new StringModuleInstanceIdentifierCodec( + controllerContext.getGlobalSchema()); + return codec.deserialize((String) input); + } + } else { + final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = + TypeDefinitionAwareCodec.from(type); + if (typeAwarecodec != null) { + if (input instanceof IdentityValuesDTO) { + return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue()); + } + return typeAwarecodec.deserialize(String.valueOf(input)); + } else { + LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName()); + // FIXME: this should be a hard error + return null; + } + } + } catch (final ClassCastException e) { // TODO remove this catch when everyone use codecs + LOG.error("ClassCastException was thrown when codec is invoked with parameter {}", input, e); + // FIXME: this should be a hard error + return null; + } + } + + @SuppressWarnings("unchecked") + @Override + @SuppressFBWarnings(value = "NP_NONNULL_RETURN_VIOLATION", justification = "legacy code") + public Object serialize(final Object input) { + try { + if (type instanceof IdentityrefTypeDefinition) { + return identityrefCodec.serialize(input); + } else if (type instanceof LeafrefTypeDefinition) { + return LEAFREF_DEFAULT_CODEC.serialize(input); + } else if (type instanceof InstanceIdentifierTypeDefinition) { + return instanceIdentifier.serialize(input); + } else { + final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = + TypeDefinitionAwareCodec.from(type); + if (typeAwarecodec != null) { + return typeAwarecodec.serialize(input); + } else { + LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName()); + return null; + } + } + } catch (final ClassCastException e) { + // FIXME: remove this catch when everyone use codecs + LOG.error("ClassCastException was thrown when codec is invoked with parameter {}", input, e); + // FIXME: this should be a hard error + return input; + } + } + + } + + public static class IdentityrefCodecImpl implements IdentityrefCodec<IdentityValuesDTO> { + private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class); + + private final DOMMountPoint mountPoint; + private final ControllerContext controllerContext; + + public IdentityrefCodecImpl(final DOMMountPoint mountPoint, final ControllerContext controllerContext) { + this.mountPoint = mountPoint; + this.controllerContext = controllerContext; + } + + @Override + public IdentityValuesDTO serialize(final QName data) { + return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), null, null); + } + + @Override + @SuppressFBWarnings(value = "NP_NONNULL_RETURN_VIOLATION", justification = "See FIXME below") + public QName deserialize(final IdentityValuesDTO data) { + final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); + final Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint, + controllerContext); + if (module == null) { + // FIXME: this should be a hard error + LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace()); + LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace)); + return null; + } + + return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue()); + } + } + + public static class LeafrefCodecImpl implements LeafrefCodec<String> { + + @Override + public String serialize(final Object data) { + return String.valueOf(data); + } + + @Override + public Object deserialize(final String data) { + return data; + } + + } + + public static class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec<IdentityValuesDTO> { + private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class); + + private final DOMMountPoint mountPoint; + private final ControllerContext controllerContext; + + public InstanceIdentifierCodecImpl(final DOMMountPoint mountPoint, + final ControllerContext controllerContext) { + this.mountPoint = mountPoint; + this.controllerContext = controllerContext; + } + + @Override + public IdentityValuesDTO serialize(final YangInstanceIdentifier data) { + final IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(); + for (final PathArgument pathArgument : data.getPathArguments()) { + final IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType()); + if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) { + final List<Predicate> predicates = + keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument).entrySet()); + identityValue.setPredicates(predicates); + } else if (pathArgument instanceof NodeWithValue && identityValue != null) { + final List<Predicate> predicates = new ArrayList<>(); + final String value = String.valueOf(((NodeWithValue<?>) pathArgument).getValue()); + predicates.add(new Predicate(null, value)); + identityValue.setPredicates(predicates); + } + identityValuesDTO.add(identityValue); + } + return identityValuesDTO; + } + + @SuppressFBWarnings(value = { "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", "NP_NONNULL_RETURN_VIOLATION" }, + justification = "Unrecognised NullableDecl") + @Override + public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) { + final List<PathArgument> result = new ArrayList<>(); + final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); + final Module module = getModuleByNamespace(valueWithNamespace.getNamespace(), mountPoint, + controllerContext); + if (module == null) { + LOG.info("Module by namespace '{}' of first node in instance-identifier was not found.", + valueWithNamespace.getNamespace()); + LOG.info("Instance-identifier will be translated as NULL for data - {}", + String.valueOf(valueWithNamespace.getValue())); + // FIXME: this should be a hard error + return null; + } + + DataNodeContainer parentContainer = module; + final List<IdentityValue> identities = data.getValuesWithNamespaces(); + for (int i = 0; i < identities.size(); i++) { + final IdentityValue identityValue = identities.get(i); + XMLNamespace validNamespace = resolveValidNamespace(identityValue.getNamespace(), mountPoint, + controllerContext); + final var found = ControllerContext.findInstanceDataChildByNameAndNamespace( + parentContainer, identityValue.getValue(), validNamespace); + if (found == null) { + LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes()); + LOG.info("Instance-identifier will be translated as NULL for data - {}", + String.valueOf(identityValue.getValue())); + // FIXME: this should be a hard error + return null; + } + final DataSchemaNode node = found.child; + final QName qName = node.getQName(); + PathArgument pathArgument = null; + if (identityValue.getPredicates().isEmpty()) { + pathArgument = new NodeIdentifier(qName); + } else { + if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry + final Predicate leafListPredicate = identityValue.getPredicates().get(0); + if (!leafListPredicate.isLeafList()) { + LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\""); + LOG.info("Instance-identifier will be translated as NULL for data - {}", + String.valueOf(identityValue.getValue())); + // FIXME: this should be a hard error + return null; + } + pathArgument = new NodeWithValue<>(qName, leafListPredicate.getValue()); + } else if (node instanceof ListSchemaNode) { // predicates are keys of list + final DataNodeContainer listNode = (DataNodeContainer) node; + final Map<QName, Object> predicatesMap = new HashMap<>(); + for (final Predicate predicate : identityValue.getPredicates()) { + validNamespace = resolveValidNamespace(predicate.getName().getNamespace(), mountPoint, + controllerContext); + final var listKey = ControllerContext + .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(), + validNamespace); + predicatesMap.put(listKey.child.getQName(), predicate.getValue()); + } + pathArgument = NodeIdentifierWithPredicates.of(qName, predicatesMap); + } else { + LOG.info("Node {} is not List or Leaf-list.", node); + LOG.info("Instance-identifier will be translated as NULL for data - {}", + String.valueOf(identityValue.getValue())); + // FIXME: this should be a hard error + return null; + } + } + result.add(pathArgument); + if (i < identities.size() - 1) { // last element in instance-identifier can be other than + // DataNodeContainer + if (node instanceof DataNodeContainer) { + parentContainer = (DataNodeContainer) node; + } else { + LOG.info("Node {} isn't instance of DataNodeContainer", node); + LOG.info("Instance-identifier will be translated as NULL for data - {}", + String.valueOf(identityValue.getValue())); + // FIXME: this should be a hard error + return null; + } + } + } + + return result.isEmpty() ? null : YangInstanceIdentifier.create(result); + } + + private static List<Predicate> keyValuesToPredicateList(final Set<Entry<QName, Object>> keyValues) { + final List<Predicate> result = new ArrayList<>(); + for (final Entry<QName, Object> entry : keyValues) { + final QName qualifiedName = entry.getKey(); + final Object value = entry.getValue(); + result.add(new Predicate(qNameToIdentityValue(qualifiedName), String.valueOf(value))); + } + return result; + } + + private static IdentityValue qNameToIdentityValue(final QName qualifiedName) { + if (qualifiedName != null) { + return new IdentityValue(qualifiedName.getNamespace().toString(), qualifiedName.getLocalName()); + } + return null; + } + } + + private static Module getModuleByNamespace(final String namespace, final DOMMountPoint mountPoint, + final ControllerContext controllerContext) { + final XMLNamespace validNamespace = resolveValidNamespace(namespace, mountPoint, controllerContext); + + Module module = null; + if (mountPoint != null) { + module = ControllerContext.findModuleByNamespace(mountPoint, validNamespace); + } else { + module = controllerContext.findModuleByNamespace(validNamespace); + } + if (module == null) { + LOG.info("Module for namespace {} was not found.", validNamespace); + return null; + } + return module; + } + + private static XMLNamespace resolveValidNamespace(final String namespace, final DOMMountPoint mountPoint, + final ControllerContext controllerContext) { + XMLNamespace validNamespace; + if (mountPoint != null) { + validNamespace = ControllerContext.findNamespaceByModuleName(mountPoint, namespace); + } else { + validNamespace = controllerContext.findNamespaceByModuleName(namespace); + } + if (validNamespace == null) { + validNamespace = XMLNamespace.of(namespace); + } + + return validNamespace; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java new file mode 100644 index 0000000..b3a1a9c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java @@ -0,0 +1,1546 @@ +/* + * Copyright (c) 2014 - 2016 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Predicates; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.net.URI; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext.FoundChild; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer; +import org.opendaylight.restconf.common.OperationsContent; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.util.OperationsResourceUtils; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.Empty; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.data.tree.api.ModifiedNodeDoesNotExistException; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.FeatureDefinition; +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.Module; +import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +public final class RestconfImpl implements RestconfService { + /** + * Notifications are served on port 8181. + */ + private static final int NOTIFICATION_PORT = 8181; + + private static final int CHAR_NOT_FOUND = -1; + + private static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; + + private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class); + + private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION; + + private static final XMLNamespace NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = + XMLNamespace.of("urn:sal:restconf:event:subscription"); + + private static final String DATASTORE_PARAM_NAME = "datastore"; + + private static final String SCOPE_PARAM_NAME = "scope"; + + private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type"; + + private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0"; + + private static final String NETCONF_BASE_PAYLOAD_NAME = "data"; + + private static final QName NETCONF_BASE_QNAME = QName.create(QNameModule.create(XMLNamespace.of(NETCONF_BASE)), + NETCONF_BASE_PAYLOAD_NAME).intern(); + + private static final QNameModule SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + Revision.of("2014-07-08")); + + private static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = + new AugmentationIdentifier(ImmutableSet.of( + QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"), + QName.create(SAL_REMOTE_AUGMENT, "notification-output-type"))); + + public static final String DATA_SUBSCR = "data-change-event-subscription"; + private static final String CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR; + + public static final String NOTIFICATION_STREAM = "notification-stream"; + private static final String CREATE_NOTIFICATION_STREAM = "create-" + NOTIFICATION_STREAM; + + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder() + .appendValue(ChronoField.YEAR, 4).appendLiteral('-') + .appendValue(ChronoField.MONTH_OF_YEAR, 2).appendLiteral('-') + .appendValue(ChronoField.DAY_OF_MONTH, 2).appendLiteral('T') + .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':') + .appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':') + .appendValue(ChronoField.SECOND_OF_MINUTE, 2) + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true) + .appendOffset("+HH:MM", "Z").toFormatter(); + + private final BrokerFacade broker; + + private final ControllerContext controllerContext; + + @Inject + public RestconfImpl(final BrokerFacade broker, final ControllerContext controllerContext) { + this.broker = broker; + this.controllerContext = controllerContext; + } + + /** + * Factory method. + * + * @deprecated Just use {@link #RestconfImpl(BrokerFacade, ControllerContext)} constructor instead. + */ + @Deprecated + public static RestconfImpl newInstance(final BrokerFacade broker, final ControllerContext controllerContext) { + return new RestconfImpl(broker, controllerContext); + } + + @Override + @Deprecated + public NormalizedNodeContext getModules(final UriInfo uriInfo) { + final Module restconfModule = getRestconfModule(); + final var stack = SchemaInferenceStack.of(controllerContext.getGlobalSchema()); + final var restconf = QName.create(restconfModule.getQNameModule(), + Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE); + stack.enterGrouping(restconf); + stack.enterSchemaTree(restconf); + final var modules = QName.create(restconf, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + final var modulesSchemaNode = stack.enterSchemaTree(modules); + checkState(modulesSchemaNode instanceof ContainerSchemaNode); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); + moduleContainerBuilder.withChild(makeModuleMapNode(controllerContext.getAllModules())); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofStack(stack, null), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + } + + /** + * Valid only for mount point. + */ + @Override + @Deprecated + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { + if (!identifier.contains(ControllerContext.MOUNT)) { + final String errMsg = "URI has bad format. If modules behind mount point should be showed," + + " URI has to end with " + ControllerContext.MOUNT; + LOG.debug("{} for {}", errMsg, identifier); + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final InstanceIdentifierContext mountPointIdentifier = + controllerContext.toMountPointIdentifier(identifier); + final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint(); + final MapNode mountPointModulesMap = makeModuleMapNode(controllerContext.getAllModules(mountPoint)); + + final Module restconfModule = getRestconfModule(); + final var stack = SchemaInferenceStack.of(controllerContext.getGlobalSchema()); + final var restconf = QName.create(restconfModule.getQNameModule(), + Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE); + stack.enterGrouping(restconf); + stack.enterSchemaTree(restconf); + final var modules = QName.create(restconf, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); + final var modulesSchemaNode = stack.enterSchemaTree(modules); + checkState(modulesSchemaNode instanceof ContainerSchemaNode); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) modulesSchemaNode); + moduleContainerBuilder.withChild(mountPointModulesMap); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofStack(stack, null), + moduleContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + } + + @Override + @Deprecated + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { + final Entry<String, Revision> nameRev = getModuleNameAndRevision(requireNonNull(identifier)); + final Module module; + final DOMMountPoint mountPoint; + if (identifier.contains(ControllerContext.MOUNT)) { + final InstanceIdentifierContext mountPointIdentifier = + controllerContext.toMountPointIdentifier(identifier); + mountPoint = mountPointIdentifier.getMountPoint(); + module = controllerContext.findModuleByNameAndRevision(mountPoint, nameRev.getKey(), + nameRev.getValue()); + } else { + mountPoint = null; + module = controllerContext.findModuleByNameAndRevision(nameRev.getKey(), nameRev.getValue()); + } + + if (module == null) { + LOG.debug("Module with name '{}' and revision '{}' was not found.", nameRev.getKey(), nameRev.getValue()); + throw new RestconfDocumentedException("Module with name '" + nameRev.getKey() + "' and revision '" + + nameRev.getValue() + "' was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); + } + + final Module restconfModule = getRestconfModule(); + final var stack = SchemaInferenceStack.of(controllerContext.getGlobalSchema()); + final var restconf = QName.create(restconfModule.getQNameModule(), + Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE); + stack.enterGrouping(restconf); + stack.enterSchemaTree(restconf); + stack.enterSchemaTree(QName.create(restconf, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE)); + stack.enterSchemaTree(QName.create(restconf, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE)); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofStack(stack, mountPoint), + makeModuleMapNode(Set.of(module)), QueryParametersParser.parseWriterParameters(uriInfo)); + } + + @Override + @Deprecated + public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) { + final Set<String> availableStreams = Notificator.getStreamNames(); + final Module restconfModule = getRestconfModule(); + final DataSchemaNode streamSchemaNode = controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); + checkState(streamSchemaNode instanceof ListSchemaNode); + + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> listStreamsBuilder = + SchemaAwareBuilders.mapBuilder((ListSchemaNode) streamSchemaNode); + + for (final String streamName : availableStreams) { + listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode)); + } + + final var stack = SchemaInferenceStack.of(controllerContext.getGlobalSchema()); + final var restconf = QName.create(restconfModule.getQNameModule(), + Draft02.RestConfModule.RESTCONF_GROUPING_SCHEMA_NODE); + stack.enterGrouping(restconf); + stack.enterSchemaTree(restconf); + final var streams = QName.create(restconf, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); + final var streamsContainerSchemaNode = stack.enterSchemaTree(streams); + checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode); + streamsContainerBuilder.withChild(listStreamsBuilder.build()); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofStack(stack), + streamsContainerBuilder.build(), QueryParametersParser.parseWriterParameters(uriInfo)); + } + + @Override + @Deprecated + public String getOperationsJSON() { + return OperationsContent.JSON.bodyFor(controllerContext.getGlobalSchema()); + } + + @Override + @Deprecated + public String getOperationsXML() { + return OperationsContent.XML.bodyFor(controllerContext.getGlobalSchema()); + } + + @Override + @Deprecated + public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) { + if (!identifier.contains(ControllerContext.MOUNT)) { + final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to " + + " end with " + ControllerContext.MOUNT; + LOG.debug("{} for {}", errMsg, identifier); + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier); + final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint(); + final var entry = OperationsResourceUtils.contextForModelContext(modelContext(mountPoint), mountPoint); + return new NormalizedNodeContext(entry.getKey(), entry.getValue()); + } + + private Module getRestconfModule() { + final Module restconfModule = controllerContext.getRestconfModule(); + if (restconfModule == null) { + LOG.debug("ietf-restconf module was not found."); + throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION, + ErrorTag.OPERATION_NOT_SUPPORTED); + } + + return restconfModule; + } + + private static Entry<String, Revision> getModuleNameAndRevision(final String identifier) { + final int mountIndex = identifier.indexOf(ControllerContext.MOUNT); + String moduleNameAndRevision = ""; + if (mountIndex >= 0) { + moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length()); + } else { + moduleNameAndRevision = identifier; + } + + final Splitter splitter = Splitter.on('/').omitEmptyStrings(); + final List<String> pathArgs = splitter.splitToList(moduleNameAndRevision); + if (pathArgs.size() < 2) { + LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' {}", identifier); + throw new RestconfDocumentedException( + "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL, + ErrorTag.INVALID_VALUE); + } + + try { + return Map.entry(pathArgs.get(0), Revision.of(pathArgs.get(1))); + } catch (final DateTimeParseException e) { + LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' {}", identifier); + throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'", + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, e); + } + } + + @Override + public Object getRoot() { + return null; + } + + @Override + public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + if (payload == null) { + // no payload specified, reroute this to no payload invokeRpc implementation + return invokeRpc(identifier, uriInfo); + } + + final SchemaNode schema = payload.getInstanceIdentifierContext().getSchemaNode(); + final ListenableFuture<? extends DOMRpcResult> response; + final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); + final NormalizedNode input = nonnullInput(schema, payload.getData()); + final EffectiveModelContext schemaContext; + + if (mountPoint != null) { + final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class); + if (mountRpcServices.isEmpty()) { + LOG.debug("Error: Rpc service is missing."); + throw new RestconfDocumentedException("Rpc service is missing."); + } + schemaContext = modelContext(mountPoint); + response = mountRpcServices.get().invokeRpc(schema.getQName(), input); + } else { + final XMLNamespace namespace = schema.getQName().getNamespace(); + if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) { + if (identifier.contains(CREATE_DATA_SUBSCR)) { + response = invokeSalRemoteRpcSubscribeRPC(payload); + } else if (identifier.contains(CREATE_NOTIFICATION_STREAM)) { + response = invokeSalRemoteRpcNotifiStrRPC(payload); + } else { + final String msg = "Not supported operation"; + LOG.warn(msg); + throw new RestconfDocumentedException(msg, ErrorType.RPC, ErrorTag.OPERATION_NOT_SUPPORTED); + } + } else { + response = broker.invokeRpc(schema.getQName(), input); + } + schemaContext = controllerContext.getGlobalSchema(); + } + + final DOMRpcResult result = checkRpcResponse(response); + + final NormalizedNode resultData; + if (result != null && result.getResult() != null) { + resultData = result.getResult(); + } else { + resultData = null; + } + + if (resultData != null && ((ContainerNode) resultData).isEmpty()) { + throw new WebApplicationException(Response.Status.NO_CONTENT); + } + + final var resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode(); + return new NormalizedNodeContext( + InstanceIdentifierContext.ofRpcOutput(schemaContext, resultNodeSchema, mountPoint), resultData, + QueryParametersParser.parseWriterParameters(uriInfo)); + } + + @SuppressFBWarnings(value = "NP_LOAD_OF_KNOWN_NULL_VALUE", + justification = "Looks like a false positive, see below FIXME") + private NormalizedNodeContext invokeRpc(final String identifier, final UriInfo uriInfo) { + final DOMMountPoint mountPoint; + final String identifierEncoded; + final EffectiveModelContext schemaContext; + if (identifier.contains(ControllerContext.MOUNT)) { + // mounted RPC call - look up mount instance. + final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier); + mountPoint = mountPointId.getMountPoint(); + schemaContext = modelContext(mountPoint); + final int startOfRemoteRpcName = + identifier.lastIndexOf(ControllerContext.MOUNT) + ControllerContext.MOUNT.length() + 1; + final String remoteRpcName = identifier.substring(startOfRemoteRpcName); + identifierEncoded = remoteRpcName; + + } else if (identifier.indexOf('/') == CHAR_NOT_FOUND) { + identifierEncoded = identifier; + mountPoint = null; + schemaContext = controllerContext.getGlobalSchema(); + } else { + LOG.debug("Identifier {} cannot contain slash character (/).", identifier); + throw new RestconfDocumentedException(String.format("Identifier %n%s%ncan\'t contain slash character (/).%n" + + "If slash is part of identifier name then use %%2F placeholder.", identifier), ErrorType.PROTOCOL, + ErrorTag.INVALID_VALUE); + } + + final String identifierDecoded = ControllerContext.urlPathArgDecode(identifierEncoded); + final RpcDefinition rpc; + if (mountPoint == null) { + rpc = controllerContext.getRpcDefinition(identifierDecoded); + } else { + rpc = findRpc(modelContext(mountPoint), identifierDecoded); + } + + if (rpc == null) { + LOG.debug("RPC {} does not exist.", identifierDecoded); + throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT); + } + + if (!rpc.getInput().getChildNodes().isEmpty()) { + LOG.debug("No input specified for RPC {} with an input section", rpc); + throw new RestconfDocumentedException("No input specified for RPC " + rpc + + " with an input section defined", ErrorType.RPC, ErrorTag.MISSING_ELEMENT); + } + + final ContainerNode input = defaultInput(rpc.getQName()); + final ListenableFuture<? extends DOMRpcResult> response; + if (mountPoint != null) { + final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class); + if (mountRpcServices.isEmpty()) { + throw new RestconfDocumentedException("Rpc service is missing."); + } + response = mountRpcServices.get().invokeRpc(rpc.getQName(), input); + } else { + response = broker.invokeRpc(rpc.getQName(), input); + } + + final NormalizedNode result = checkRpcResponse(response).getResult(); + if (result != null && ((ContainerNode) result).isEmpty()) { + throw new WebApplicationException(Response.Status.NO_CONTENT); + } + + // FIXME: in reference to the above @SupressFBWarnings: "mountPoint" reference here trips up SpotBugs, as it + // thinks it can only ever be null. Except it can very much be non-null. The core problem is the horrible + // structure of this code where we have a sh*tload of checks for mountpoint above and all over the + // codebase where all that difference should have been properly encapsulated. + // + // This is legacy code, so if anybody cares to do that refactor, feel free to contribute, but I am not + // doing that work. + final var iic = mountPoint == null ? InstanceIdentifierContext.ofLocalRpcOutput(schemaContext, rpc) + : InstanceIdentifierContext.ofMountPointRpcOutput(mountPoint, schemaContext, rpc); + return new NormalizedNodeContext(iic, result, QueryParametersParser.parseWriterParameters(uriInfo)); + } + + private static @NonNull NormalizedNode nonnullInput(final SchemaNode rpc, final NormalizedNode input) { + return input != null ? input : defaultInput(rpc.getQName()); + } + + private static @NonNull ContainerNode defaultInput(final QName rpcName) { + return ImmutableNodes.containerNode(YangConstants.operationInputQName(rpcName.getModule())); + } + + @SuppressWarnings("checkstyle:avoidHidingCauseException") + private static DOMRpcResult checkRpcResponse(final ListenableFuture<? extends DOMRpcResult> response) { + if (response == null) { + return null; + } + try { + final DOMRpcResult retValue = response.get(); + if (retValue.getErrors().isEmpty()) { + return retValue; + } + LOG.debug("RpcError message {}", retValue.getErrors()); + throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors()); + } catch (final InterruptedException e) { + final String errMsg = "The operation was interrupted while executing and did not complete."; + LOG.debug("Rpc Interrupt - {}", errMsg, e); + throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION, e); + } catch (final ExecutionException e) { + LOG.debug("Execution RpcError: ", e); + Throwable cause = e.getCause(); + if (cause == null) { + throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.", + e); + } + while (cause.getCause() != null) { + cause = cause.getCause(); + } + + if (cause instanceof IllegalArgumentException) { + throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } else if (cause instanceof DOMRpcImplementationNotAvailableException) { + throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, + ErrorTag.OPERATION_NOT_SUPPORTED); + } + throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.", + cause); + } catch (final CancellationException e) { + final String errMsg = "The operation was cancelled while executing."; + LOG.debug("Cancel RpcExecution: {}", errMsg, e); + throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION); + } + } + + private static void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) { + if (inputSchema != null && payload.getData() == null) { + // expected a non null payload + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } else if (inputSchema == null && payload.getData() != null) { + // did not expect any input + throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + } + + private ListenableFuture<DOMRpcResult> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) { + final ContainerNode value = (ContainerNode) payload.getData(); + final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName(); + final Optional<DataContainerChild> path = + value.findChildByArg(new NodeIdentifier(QName.create(rpcQName, "path"))); + final Object pathValue = path.isPresent() ? path.get().body() : null; + + if (!(pathValue instanceof YangInstanceIdentifier)) { + LOG.debug("Instance identifier {} was not normalized correctly", rpcQName); + throw new RestconfDocumentedException("Instance identifier was not normalized correctly", + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED); + } + + final YangInstanceIdentifier pathIdentifier = (YangInstanceIdentifier) pathValue; + final String streamName; + NotificationOutputType outputType = null; + if (!pathIdentifier.isEmpty()) { + final String fullRestconfIdentifier = + DATA_SUBSCR + controllerContext.toFullRestconfIdentifier(pathIdentifier, null); + + LogicalDatastoreType datastore = + parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); + datastore = datastore == null ? DEFAULT_DATASTORE : datastore; + + Scope scope = parseEnumTypeParameter(value, Scope.class, SCOPE_PARAM_NAME); + scope = scope == null ? Scope.BASE : scope; + + outputType = parseEnumTypeParameter(value, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME); + outputType = outputType == null ? NotificationOutputType.XML : outputType; + + streamName = Notificator + .createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore + "/scope=" + scope); + } else { + streamName = CREATE_DATA_SUBSCR; + } + + if (Strings.isNullOrEmpty(streamName)) { + LOG.debug("Path is empty or contains value node which is not Container or List built-in type at {}", + pathIdentifier); + throw new RestconfDocumentedException("Path is empty or contains value node which is not Container or List " + + "built-in type.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + if (!Notificator.existListenerFor(streamName)) { + Notificator.createListener(pathIdentifier, streamName, outputType, controllerContext); + } + + return Futures.immediateFuture(new DefaultDOMRpcResult(Builders.containerBuilder() + .withNodeIdentifier(new NodeIdentifier(QName.create(rpcQName, "output"))) + .withChild(ImmutableNodes.leafNode(QName.create(rpcQName, "stream-name"), streamName)) + .build())); + } + + private static RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) { + final String[] splittedIdentifier = identifierDecoded.split(":"); + if (splittedIdentifier.length != 2) { + LOG.debug("{} could not be split to 2 parts (module:rpc name)", identifierDecoded); + throw new RestconfDocumentedException(identifierDecoded + " could not be split to 2 parts " + + "(module:rpc name)", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); + } + for (final Module module : schemaContext.getModules()) { + if (module.getName().equals(splittedIdentifier[0])) { + for (final RpcDefinition rpcDefinition : module.getRpcs()) { + if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) { + return rpcDefinition; + } + } + } + } + return null; + } + + @Override + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + boolean withDefaUsed = false; + String withDefa = null; + + for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "with-defaults": + if (!withDefaUsed) { + withDefaUsed = true; + withDefa = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("With-defaults parameter can be used only once."); + } + break; + default: + LOG.info("Unknown key : {}.", entry.getKey()); + break; + } + } + + // TODO: this flag is always ignored + boolean tagged = false; + if (withDefaUsed) { + if ("report-all-tagged".equals(withDefa)) { + tagged = true; + withDefa = null; + } + if ("report-all".equals(withDefa)) { + withDefa = null; + } + } + + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); + NormalizedNode data = null; + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + if (mountPoint != null) { + data = broker.readConfigurationData(mountPoint, normalizedII, withDefa); + } else { + data = broker.readConfigurationData(normalizedII, withDefa); + } + if (data == null) { + throw dataMissing(identifier); + } + return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo)); + } + + @Override + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); + NormalizedNode data = null; + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + if (mountPoint != null) { + data = broker.readOperationalData(mountPoint, normalizedII); + } else { + data = broker.readOperationalData(normalizedII); + } + if (data == null) { + throw dataMissing(identifier); + } + return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo)); + } + + private static RestconfDocumentedException dataMissing(final String identifier) { + LOG.debug("Request could not be completed because the relevant data model content does not exist {}", + identifier); + return new RestconfDocumentedException("Request could not be completed because the relevant data model content " + + "does not exist", ErrorType.APPLICATION, ErrorTag.DATA_MISSING); + } + + @Override + public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + boolean insertUsed = false; + boolean pointUsed = false; + String insert = null; + String point = null; + + for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "insert": + if (!insertUsed) { + insertUsed = true; + insert = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Insert parameter can be used only once."); + } + break; + case "point": + if (!pointUsed) { + pointUsed = true; + point = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Point parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey()); + } + } + + if (pointUsed && !insertUsed) { + throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter."); + } + if (pointUsed && (insert.equals("first") || insert.equals("last"))) { + throw new RestconfDocumentedException( + "Point parameter can be used only with 'after' or 'before' values of Insert parameter."); + } + + requireNonNull(identifier); + + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + + validateInput(iiWithData.getSchemaNode(), payload); + validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier()); + validateListKeysEqualityInPayloadAndUri(payload); + + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + + /* + * There is a small window where another write transaction could be + * updating the same data simultaneously and we get an + * OptimisticLockFailedException. This error is likely transient and The + * WriteTransaction#submit API docs state that a retry will likely + * succeed. So we'll try again if that scenario occurs. If it fails a + * third time then it probably will never succeed so we'll fail in that + * case. + * + * By retrying we're attempting to hide the internal implementation of + * the data store and how it handles concurrent updates from the + * restconf client. The client has instructed us to put the data and we + * should make every effort to do so without pushing optimistic lock + * failures back to the client and forcing them to handle it via retry + * (and having to document the behavior). + */ + PutResult result = null; + int tries = 2; + while (true) { + if (mountPoint != null) { + + result = broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData(), insert, + point); + } else { + result = broker.commitConfigurationDataPut(controllerContext.getGlobalSchema(), normalizedII, + payload.getData(), insert, point); + } + + try { + result.getFutureOfPutData().get(); + } catch (final InterruptedException e) { + LOG.debug("Update failed for {}", identifier, e); + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { + final TransactionCommitFailedException failure = Throwables.getCauseAs(e, + TransactionCommitFailedException.class); + if (failure instanceof OptimisticLockFailedException) { + if (--tries <= 0) { + LOG.debug("Got OptimisticLockFailedException on last try - failing {}", identifier); + throw new RestconfDocumentedException(e.getMessage(), e, failure.getErrorList()); + } + + LOG.debug("Got OptimisticLockFailedException - trying again {}", identifier); + continue; + } + + LOG.debug("Update failed for {}", identifier, e); + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), failure); + } + + return Response.status(result.getStatus()).build(); + } + } + + private static void validateTopLevelNodeName(final NormalizedNodeContext node, + final YangInstanceIdentifier identifier) { + + final String payloadName = node.getData().getIdentifier().getNodeType().getLocalName(); + + // no arguments + if (identifier.isEmpty()) { + // no "data" payload + if (!node.getData().getIdentifier().getNodeType().equals(NETCONF_BASE_QNAME)) { + throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + // any arguments + } else { + final String identifierName = identifier.getLastPathArgument().getNodeType().getLocalName(); + if (!payloadName.equals(identifierName)) { + throw new RestconfDocumentedException( + "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + } + } + + /** + * Validates whether keys in {@code payload} are equal to values of keys in + * {@code iiWithData} for list schema node. + * + * @throws RestconfDocumentedException + * if key values or key count in payload and URI isn't equal + * + */ + private static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) { + checkArgument(payload != null); + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument(); + final SchemaNode schemaNode = iiWithData.getSchemaNode(); + final NormalizedNode data = payload.getData(); + if (schemaNode instanceof ListSchemaNode) { + final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition(); + if (lastPathArgument instanceof NodeIdentifierWithPredicates && data instanceof MapEntryNode) { + final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument).asMap(); + isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions); + } + } + } + + @VisibleForTesting + public static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload, + final List<QName> keyDefinitions) { + + final Map<QName, Object> mutableCopyUriKeyValues = new HashMap<>(uriKeyValues); + for (final QName keyDefinition : keyDefinitions) { + final Object uriKeyValue = RestconfDocumentedException.throwIfNull( + // should be caught during parsing URI to InstanceIdentifier + mutableCopyUriKeyValues.remove(keyDefinition), ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, + "Missing key %s in URI.", keyDefinition); + + final Object dataKeyValue = payload.getIdentifier().getValue(keyDefinition); + + if (!Objects.deepEquals(uriKeyValue, dataKeyValue)) { + final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() + + "' specified in the URI doesn't match the value '" + dataKeyValue + + "' specified in the message body. "; + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + } + } + + @Override + public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + return createConfigurationData(payload, uriInfo); + } + + @Override + public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) { + if (payload == null) { + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + + boolean insertUsed = false; + boolean pointUsed = false; + String insert = null; + String point = null; + + if (uriInfo != null) { + for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "insert": + if (!insertUsed) { + insertUsed = true; + insert = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Insert parameter can be used only once."); + } + break; + case "point": + if (!pointUsed) { + pointUsed = true; + point = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Point parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey()); + } + } + } + + if (pointUsed && !insertUsed) { + throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter."); + } + if (pointUsed && (insert.equals("first") || insert.equals("last"))) { + throw new RestconfDocumentedException( + "Point parameter can be used only with 'after' or 'before' values of Insert parameter."); + } + + FluentFuture<? extends CommitInfo> future; + if (mountPoint != null) { + future = broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert, + point); + } else { + future = broker.commitConfigurationDataPost(controllerContext.getGlobalSchema(), normalizedII, + payload.getData(), insert, point); + } + + try { + future.get(); + } catch (final InterruptedException e) { + LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e); + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { + LOG.info("Error creating data {}", uriInfo != null ? uriInfo.getPath() : "", e); + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e, + TransactionCommitFailedException.class)); + } + + LOG.trace("Successfuly created data."); + + final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT); + // FIXME: Provide path to result. + final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII); + if (location != null) { + responseBuilder.location(location); + } + return responseBuilder.build(); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, + final YangInstanceIdentifier normalizedII) { + if (uriInfo == null) { + // This is null if invoked internally + return null; + } + + final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); + uriBuilder.path("config"); + try { + uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint)); + } catch (final Exception e) { + LOG.info("Location for instance identifier {} was not created", normalizedII, e); + return null; + } + return uriBuilder.build(); + } + + @Override + public Response deleteConfigurationData(final String identifier) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); + + final FluentFuture<? extends CommitInfo> future; + if (mountPoint != null) { + future = broker.commitConfigurationDataDelete(mountPoint, normalizedII); + } else { + future = broker.commitConfigurationDataDelete(normalizedII); + } + + try { + future.get(); + } catch (final InterruptedException e) { + throw new RestconfDocumentedException(e.getMessage(), e); + } catch (final ExecutionException e) { + final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e), + Predicates.instanceOf(ModifiedNodeDoesNotExistException.class)).toJavaUtil(); + if (searchedException.isPresent()) { + throw new RestconfDocumentedException("Data specified for delete doesn't exist.", ErrorType.APPLICATION, + ErrorTag.DATA_MISSING, e); + } + + throw RestconfDocumentedException.decodeAndThrow(e.getMessage(), Throwables.getCauseAs(e, + TransactionCommitFailedException.class)); + } + + return Response.status(Status.OK).build(); + } + + /** + * Subscribes to some path in schema context (stream) to listen on changes + * on this stream. + * + * <p> + * Additional parameters for subscribing to stream are loaded via rpc input + * parameters: + * <ul> + * <li>datastore - default CONFIGURATION (other values of + * {@link LogicalDatastoreType} enum type)</li> + * <li>scope - default BASE (other values of {@link Scope})</li> + * </ul> + */ + @Override + public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { + boolean startTimeUsed = false; + boolean stopTimeUsed = false; + Instant start = Instant.now(); + Instant stop = null; + boolean filterUsed = false; + String filter = null; + boolean leafNodesOnlyUsed = false; + boolean leafNodesOnly = false; + boolean skipNotificationDataUsed = false; + boolean skipNotificationData = false; + + for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) { + switch (entry.getKey()) { + case "start-time": + if (!startTimeUsed) { + startTimeUsed = true; + start = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Start-time parameter can be used only once."); + } + break; + case "stop-time": + if (!stopTimeUsed) { + stopTimeUsed = true; + stop = parseDateFromQueryParam(entry); + } else { + throw new RestconfDocumentedException("Stop-time parameter can be used only once."); + } + break; + case "filter": + if (!filterUsed) { + filterUsed = true; + filter = entry.getValue().iterator().next(); + } else { + throw new RestconfDocumentedException("Filter parameter can be used only once."); + } + break; + case "odl-leaf-nodes-only": + if (!leafNodesOnlyUsed) { + leafNodesOnlyUsed = true; + leafNodesOnly = Boolean.parseBoolean(entry.getValue().iterator().next()); + } else { + throw new RestconfDocumentedException("Odl-leaf-nodes-only parameter can be used only once."); + } + break; + case "odl-skip-notification-data": + if (!skipNotificationDataUsed) { + skipNotificationDataUsed = true; + skipNotificationData = Boolean.parseBoolean(entry.getValue().iterator().next()); + } else { + throw new RestconfDocumentedException( + "Odl-skip-notification-data parameter can be used only once."); + } + break; + default: + throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey()); + } + } + if (!startTimeUsed && stopTimeUsed) { + throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter."); + } + URI response = null; + if (identifier.contains(DATA_SUBSCR)) { + response = dataSubs(identifier, uriInfo, start, stop, filter, leafNodesOnly, skipNotificationData); + } else if (identifier.contains(NOTIFICATION_STREAM)) { + response = notifStream(identifier, uriInfo, start, stop, filter); + } + + if (response != null) { + // prepare node with value of location + + final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi"); + final QName locationQName = QName.create(qnameBase, "location"); + + final var stack = SchemaInferenceStack.of(controllerContext.getGlobalSchema()); + stack.enterSchemaTree(qnameBase); + stack.enterSchemaTree(locationQName); + + // prepare new header with location + return new NormalizedNodeContext(InstanceIdentifierContext.ofStack(stack), + ImmutableNodes.leafNode(locationQName, response.toString()), ImmutableMap.of("Location", response)); + } + + final String msg = "Bad type of notification of sal-remote"; + LOG.warn(msg); + throw new RestconfDocumentedException(msg); + } + + private static Instant parseDateFromQueryParam(final Entry<String, List<String>> entry) { + final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); + final String value = event.getValue(); + final TemporalAccessor p; + try { + p = FORMATTER.parse(value); + } catch (final DateTimeParseException e) { + throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e); + } + return Instant.from(p); + } + + /** + * Register notification listener by stream name. + * + * @param identifier + * stream name + * @param uriInfo + * uriInfo + * @param stop + * stop-time of getting notification + * @param start + * start-time of getting notification + * @param filter + * indicate which subset of all possible events are of interest + * @return {@link URI} of location + */ + private URI notifStream(final String identifier, final UriInfo uriInfo, final Instant start, + final Instant stop, final String filter) { + final String streamName = Notificator.createStreamNameFromUri(identifier); + if (Strings.isNullOrEmpty(streamName)) { + throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName); + if (listeners == null || listeners.isEmpty()) { + throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, + ErrorTag.UNKNOWN_ELEMENT); + } + + for (final NotificationListenerAdapter listener : listeners) { + broker.registerToListenNotification(listener); + listener.setQueryParams(start, Optional.ofNullable(stop), Optional.ofNullable(filter), false, false); + } + + final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); + + final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(NOTIFICATION_PORT); + final int notificationPort = webSocketServerInstance.getPort(); + + + final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme(getWsScheme(uriInfo)); + + return uriToWebsocketServerBuilder.replacePath(streamName).build(); + } + + private static String getWsScheme(final UriInfo uriInfo) { + URI uri = uriInfo.getAbsolutePath(); + if (uri == null) { + return "ws"; + } + String subscriptionScheme = uri.getScheme().toLowerCase(Locale.ROOT); + return subscriptionScheme.equals("https") ? "wss" : "ws"; + } + + /** + * Register data change listener by stream name. + * + * @param identifier + * stream name + * @param uriInfo + * uri info + * @param stop + * start-time of getting notification + * @param start + * stop-time of getting notification + * @param filter + * indicate which subset of all possible events are of interest + * @return {@link URI} of location + */ + private URI dataSubs(final String identifier, final UriInfo uriInfo, final Instant start, final Instant stop, + final String filter, final boolean leafNodesOnly, final boolean skipNotificationData) { + final String streamName = Notificator.createStreamNameFromUri(identifier); + if (Strings.isNullOrEmpty(streamName)) { + throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + final ListenerAdapter listener = Notificator.getListenerFor(streamName); + if (listener == null) { + throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, + ErrorTag.UNKNOWN_ELEMENT); + } + listener.setQueryParams(start, Optional.ofNullable(stop), Optional.ofNullable(filter), leafNodesOnly, + skipNotificationData); + + final Map<String, String> paramToValues = resolveValuesFromUri(identifier); + final LogicalDatastoreType datastore = + parserURIEnumParameter(LogicalDatastoreType.class, paramToValues.get(DATASTORE_PARAM_NAME)); + if (datastore == null) { + throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)", + ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); + } + final Scope scope = parserURIEnumParameter(Scope.class, paramToValues.get(SCOPE_PARAM_NAME)); + if (scope == null) { + throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)", + ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); + } + + broker.registerToListenDataChanges(datastore, scope, listener); + + final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); + + final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(NOTIFICATION_PORT); + final int notificationPort = webSocketServerInstance.getPort(); + + final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme(getWsScheme(uriInfo)); + + return uriToWebsocketServerBuilder.replacePath(streamName).build(); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public PatchStatusContext patchConfigurationData(final String identifier, final PatchContext context, + final UriInfo uriInfo) { + if (context == null) { + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + try { + return broker.patchConfigurationDataWithinTransaction(context); + } catch (final Exception e) { + LOG.debug("Patch transaction failed", e); + throw new RestconfDocumentedException(e.getMessage(), e); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public PatchStatusContext patchConfigurationData(final PatchContext context, @Context final UriInfo uriInfo) { + if (context == null) { + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + + try { + return broker.patchConfigurationDataWithinTransaction(context); + } catch (final Exception e) { + LOG.debug("Patch transaction failed", e); + throw new RestconfDocumentedException(e.getMessage(), e); + } + } + + /** + * Load parameter for subscribing to stream from input composite node. + * + * @param value + * contains value + * @return enum object if its string value is equal to {@code paramName}. In + * other cases null. + */ + private static <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor, + final String paramName) { + final Optional<DataContainerChild> optAugNode = value.findChildByArg(SAL_REMOTE_AUG_IDENTIFIER); + if (optAugNode.isEmpty()) { + return null; + } + final DataContainerChild augNode = optAugNode.get(); + if (!(augNode instanceof AugmentationNode)) { + return null; + } + final Optional<DataContainerChild> enumNode = ((AugmentationNode) augNode).findChildByArg( + new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName))); + if (enumNode.isEmpty()) { + return null; + } + final Object rawValue = enumNode.get().body(); + if (!(rawValue instanceof String)) { + return null; + } + + return resolveAsEnum(classDescriptor, (String) rawValue); + } + + /** + * Checks whether {@code value} is one of the string representation of + * enumeration {@code classDescriptor}. + * + * @return enum object if string value of {@code classDescriptor} + * enumeration is equal to {@code value}. Other cases null. + */ + private static <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) { + if (Strings.isNullOrEmpty(value)) { + return null; + } + return resolveAsEnum(classDescriptor, value); + } + + private static <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) { + final T[] enumConstants = classDescriptor.getEnumConstants(); + if (enumConstants != null) { + for (final T enm : classDescriptor.getEnumConstants()) { + if (((Enum<?>) enm).name().equals(value)) { + return enm; + } + } + } + return null; + } + + private static Map<String, String> resolveValuesFromUri(final String uri) { + final Map<String, String> result = new HashMap<>(); + final String[] tokens = uri.split("/"); + for (int i = 1; i < tokens.length; i++) { + final String[] parameterTokens = tokens[i].split("="); + if (parameterTokens.length == 2) { + result.put(parameterTokens[0], parameterTokens[1]); + } + } + return result; + } + + private MapNode makeModuleMapNode(final Collection<? extends Module> modules) { + requireNonNull(modules); + final Module restconfModule = getRestconfModule(); + final DataSchemaNode moduleSchemaNode = controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); + checkState(moduleSchemaNode instanceof ListSchemaNode); + + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> listModuleBuilder = + SchemaAwareBuilders.mapBuilder((ListSchemaNode) moduleSchemaNode); + + for (final Module module : modules) { + listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode)); + } + return listModuleBuilder.build(); + } + + private static MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) { + checkArgument(moduleSchemaNode instanceof ListSchemaNode, + "moduleSchemaNode has to be of type ListSchemaNode"); + final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode; + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = + SchemaAwareBuilders.mapEntryBuilder(listModuleSchemaNode); + + var instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "name"); + final LeafSchemaNode nameSchemaNode = getFirstLeaf(instanceDataChildrenByName); + moduleNodeValues.withChild( + SchemaAwareBuilders.leafBuilder(nameSchemaNode).withValue(module.getName()).build()); + + final QNameModule qNameModule = module.getQNameModule(); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "revision"); + final LeafSchemaNode revisionSchemaNode = getFirstLeaf(instanceDataChildrenByName); + final Optional<Revision> revision = qNameModule.getRevision(); + moduleNodeValues.withChild(SchemaAwareBuilders.leafBuilder(revisionSchemaNode) + .withValue(revision.map(Revision::toString).orElse("")).build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "namespace"); + final LeafSchemaNode namespaceSchemaNode = getFirstLeaf(instanceDataChildrenByName); + moduleNodeValues.withChild(SchemaAwareBuilders.leafBuilder(namespaceSchemaNode) + .withValue(qNameModule.getNamespace().toString()).build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listModuleSchemaNode, "feature"); + final LeafListSchemaNode featureSchemaNode = getFirst(instanceDataChildrenByName, LeafListSchemaNode.class); + final ListNodeBuilder<Object, SystemLeafSetNode<Object>> featuresBuilder = + SchemaAwareBuilders.leafSetBuilder(featureSchemaNode); + for (final FeatureDefinition feature : module.getFeatures()) { + featuresBuilder.withChild(SchemaAwareBuilders.leafSetEntryBuilder(featureSchemaNode) + .withValue(feature.getQName().getLocalName()).build()); + } + moduleNodeValues.withChild(featuresBuilder.build()); + + return moduleNodeValues.build(); + } + + protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) { + checkArgument(streamSchemaNode instanceof ListSchemaNode, + "streamSchemaNode has to be of type ListSchemaNode"); + final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode; + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = + SchemaAwareBuilders.mapEntryBuilder(listStreamSchemaNode); + + var instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "name"); + final LeafSchemaNode nameSchemaNode = getFirstLeaf(instanceDataChildrenByName); + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder(nameSchemaNode).withValue(streamName).build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "description"); + final LeafSchemaNode descriptionSchemaNode = getFirstLeaf(instanceDataChildrenByName); + streamNodeValues.withChild(SchemaAwareBuilders.leafBuilder(descriptionSchemaNode) + .withValue("DESCRIPTION_PLACEHOLDER") + .build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-support"); + final LeafSchemaNode replaySupportSchemaNode = getFirstLeaf(instanceDataChildrenByName); + streamNodeValues.withChild(SchemaAwareBuilders.leafBuilder(replaySupportSchemaNode) + .withValue(Boolean.TRUE).build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-log-creation-time"); + final LeafSchemaNode replayLogCreationTimeSchemaNode = getFirstLeaf(instanceDataChildrenByName); + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder(replayLogCreationTimeSchemaNode).withValue("").build()); + + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "events"); + final LeafSchemaNode eventsSchemaNode = getFirstLeaf(instanceDataChildrenByName); + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder(eventsSchemaNode).withValue(Empty.value()).build()); + + return streamNodeValues.build(); + } + + /** + * Prepare stream for notification. + * + * @param payload + * contains list of qnames of notifications + * @return - checked future object + */ + private ListenableFuture<DOMRpcResult> invokeSalRemoteRpcNotifiStrRPC(final NormalizedNodeContext payload) { + final ContainerNode data = (ContainerNode) payload.getData(); + LeafSetNode leafSet = null; + String outputType = "XML"; + for (final DataContainerChild dataChild : data.body()) { + if (dataChild instanceof LeafSetNode) { + leafSet = (LeafSetNode) dataChild; + } else if (dataChild instanceof AugmentationNode) { + outputType = (String) ((AugmentationNode) dataChild).body().iterator().next().body(); + } + } + + final Collection<LeafSetEntryNode<?>> entryNodes = leafSet.body(); + final List<Absolute> paths = new ArrayList<>(); + + StringBuilder streamNameBuilder = new StringBuilder(CREATE_NOTIFICATION_STREAM).append('/'); + final Iterator<LeafSetEntryNode<?>> iterator = entryNodes.iterator(); + while (iterator.hasNext()) { + final QName valueQName = QName.create((String) iterator.next().body()); + final XMLNamespace namespace = valueQName.getModule().getNamespace(); + final Module module = controllerContext.findModuleByNamespace(namespace); + checkNotNull(module, "Module for namespace %s does not exist", namespace); + NotificationDefinition notifiDef = null; + for (final NotificationDefinition notification : module.getNotifications()) { + if (notification.getQName().equals(valueQName)) { + notifiDef = notification; + break; + } + } + final String moduleName = module.getName(); + if (notifiDef == null) { + throw new IllegalArgumentException("Notification " + valueQName + " does not exist in module " + + moduleName); + } + + paths.add(Absolute.of(notifiDef.getQName())); + streamNameBuilder.append(moduleName).append(':').append(valueQName.getLocalName()); + if (iterator.hasNext()) { + streamNameBuilder.append(','); + } + } + + final String streamName = streamNameBuilder.toString(); + final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName(); + + if (!Notificator.existNotificationListenerFor(streamName)) { + Notificator.createNotificationListener(paths, streamName, outputType, controllerContext); + } + + return Futures.immediateFuture(new DefaultDOMRpcResult(Builders.containerBuilder() + .withNodeIdentifier(new NodeIdentifier(QName.create(rpcQName, "output"))) + .withChild(ImmutableNodes.leafNode(QName.create(rpcQName, "notification-stream-identifier"), streamName)) + .build())); + } + + private static LeafSchemaNode getFirstLeaf(final List<FoundChild> children) { + return getFirst(children, LeafSchemaNode.class); + } + + private static <T extends DataSchemaNode> T getFirst(final List<FoundChild> children, final Class<T> expected) { + checkState(!children.isEmpty()); + final var first = children.get(0); + checkState(expected.isInstance(first.child)); + return expected.cast(first.child); + } + + private static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { + return mountPoint.getService(DOMSchemaService.class) + .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) + .orElse(null); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfProviderImpl.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfProviderImpl.java new file mode 100644 index 0000000..5b6608e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfProviderImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static java.util.Objects.requireNonNull; + +import java.math.BigInteger; +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; +import org.opendaylight.netconf.sal.rest.api.RestConnector; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Config; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Delete; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Get; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Operational; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Post; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Put; +import org.opendaylight.netconf.sal.restconf.impl.jmx.RestConnectorRuntimeMXBean; +import org.opendaylight.netconf.sal.restconf.impl.jmx.Rpcs; +import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; + +public class RestconfProviderImpl extends AbstractMXBean + implements AutoCloseable, RestConnector, RestConnectorRuntimeMXBean { + private final IpAddress websocketAddress; + private final PortNumber websocketPort; + private final StatisticsRestconfServiceWrapper stats; + private Thread webSocketServerThread; + + public RestconfProviderImpl(final StatisticsRestconfServiceWrapper stats, final IpAddress websocketAddress, + final PortNumber websocketPort) { + super("Draft02ProviderStatistics", "restconf-connector", null); + this.stats = requireNonNull(stats); + this.websocketAddress = requireNonNull(websocketAddress); + this.websocketPort = requireNonNull(websocketPort); + } + + public void start() { + this.webSocketServerThread = new Thread(WebSocketServer.createInstance( + websocketAddress.stringValue(), websocketPort.getValue().toJava())); + this.webSocketServerThread.setName("Web socket server on port " + websocketPort); + this.webSocketServerThread.start(); + + registerMBean(); + } + + @Override + public void close() { + WebSocketServer.destroyInstance(); + if (this.webSocketServerThread != null) { + this.webSocketServerThread.interrupt(); + } + + unregisterMBean(); + } + + @Override + public Config getConfig() { + final Config config = new Config(); + + final Get get = new Get(); + get.setReceivedRequests(this.stats.getConfigGet()); + get.setSuccessfulResponses(this.stats.getSuccessGetConfig()); + get.setFailedResponses(this.stats.getFailureGetConfig()); + config.setGet(get); + + final Post post = new Post(); + post.setReceivedRequests(this.stats.getConfigPost()); + post.setSuccessfulResponses(this.stats.getSuccessPost()); + post.setFailedResponses(this.stats.getFailurePost()); + config.setPost(post); + + final Put put = new Put(); + put.setReceivedRequests(this.stats.getConfigPut()); + put.setSuccessfulResponses(this.stats.getSuccessPut()); + put.setFailedResponses(this.stats.getFailurePut()); + config.setPut(put); + + final Delete delete = new Delete(); + delete.setReceivedRequests(this.stats.getConfigDelete()); + delete.setSuccessfulResponses(this.stats.getSuccessDelete()); + delete.setFailedResponses(this.stats.getFailureDelete()); + config.setDelete(delete); + + return config; + } + + @Override + public Operational getOperational() { + final BigInteger opGet = this.stats.getOperationalGet(); + final Operational operational = new Operational(); + final Get get = new Get(); + get.setReceivedRequests(opGet); + get.setSuccessfulResponses(this.stats.getSuccessGetOperational()); + get.setFailedResponses(this.stats.getFailureGetOperational()); + operational.setGet(get); + return operational; + } + + @Override + public Rpcs getRpcs() { + final BigInteger rpcInvoke = this.stats.getRpc(); + final Rpcs rpcs = new Rpcs(); + rpcs.setReceivedRequests(rpcInvoke); + return rpcs; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java new file mode 100644 index 0000000..afe4be0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicLong; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; + +@Singleton +public final class StatisticsRestconfServiceWrapper implements RestconfService { + + AtomicLong operationalGet = new AtomicLong(); + AtomicLong configGet = new AtomicLong(); + AtomicLong rpc = new AtomicLong(); + AtomicLong configPost = new AtomicLong(); + AtomicLong configPut = new AtomicLong(); + AtomicLong configDelete = new AtomicLong(); + AtomicLong successGetConfig = new AtomicLong(); + AtomicLong successGetOperational = new AtomicLong(); + AtomicLong successPost = new AtomicLong(); + AtomicLong successPut = new AtomicLong(); + AtomicLong successDelete = new AtomicLong(); + AtomicLong failureGetConfig = new AtomicLong(); + AtomicLong failureGetOperational = new AtomicLong(); + AtomicLong failurePost = new AtomicLong(); + AtomicLong failurePut = new AtomicLong(); + AtomicLong failureDelete = new AtomicLong(); + + private final RestconfService delegate; + + @Inject + public StatisticsRestconfServiceWrapper(final RestconfImpl delegate) { + this.delegate = delegate; + } + + /** + * Factory method. + * + * @deprecated Just use {@link #StatisticsRestconfServiceWrapper(RestconfImpl)} constructor instead. + */ + @Deprecated + public static StatisticsRestconfServiceWrapper newInstance(RestconfImpl delegate) { + return new StatisticsRestconfServiceWrapper(delegate); + } + + @Override + public Object getRoot() { + return this.delegate.getRoot(); + } + + @Override + public NormalizedNodeContext getModules(final UriInfo uriInfo) { + return this.delegate.getModules(uriInfo); + } + + @Override + public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) { + return this.delegate.getModules(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) { + return this.delegate.getModule(identifier, uriInfo); + } + + @Override + public String getOperationsJSON() { + return this.delegate.getOperationsJSON(); + } + + @Override + public String getOperationsXML() { + return this.delegate.getOperationsXML(); + } + + @Override + public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) { + return this.delegate.getOperations(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + this.rpc.incrementAndGet(); + return this.delegate.invokeRpc(identifier, payload, uriInfo); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + this.configGet.incrementAndGet(); + NormalizedNodeContext normalizedNodeContext = null; + try { + normalizedNodeContext = this.delegate.readConfigurationData(identifier, uriInfo); + if (normalizedNodeContext.getData() != null) { + this.successGetConfig.incrementAndGet(); + } else { + this.failureGetConfig.incrementAndGet(); + } + } catch (final Exception e) { + this.failureGetConfig.incrementAndGet(); + throw e; + } + return normalizedNodeContext; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) { + this.operationalGet.incrementAndGet(); + NormalizedNodeContext normalizedNodeContext = null; + try { + normalizedNodeContext = this.delegate.readOperationalData(identifier, uriInfo); + if (normalizedNodeContext.getData() != null) { + this.successGetOperational.incrementAndGet(); + } else { + this.failureGetOperational.incrementAndGet(); + } + } catch (final Exception e) { + this.failureGetOperational.incrementAndGet(); + throw e; + } + return normalizedNodeContext; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + this.configPut.incrementAndGet(); + Response response = null; + try { + response = this.delegate.updateConfigurationData(identifier, payload, uriInfo); + if (response.getStatus() == Status.OK.getStatusCode()) { + this.successPut.incrementAndGet(); + } else { + this.failurePut.incrementAndGet(); + } + } catch (final Exception e) { + this.failurePut.incrementAndGet(); + throw e; + } + return response; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, + final UriInfo uriInfo) { + this.configPost.incrementAndGet(); + Response response = null; + try { + response = this.delegate.createConfigurationData(identifier, payload, uriInfo); + if (response.getStatus() == Status.OK.getStatusCode()) { + this.successPost.incrementAndGet(); + } else { + this.failurePost.incrementAndGet(); + } + } catch (final Exception e) { + this.failurePost.incrementAndGet(); + throw e; + } + return response; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) { + this.configPost.incrementAndGet(); + Response response = null; + try { + response = this.delegate.createConfigurationData(payload, uriInfo); + if (response.getStatus() == Status.OK.getStatusCode()) { + this.successPost.incrementAndGet(); + } else { + this.failurePost.incrementAndGet(); + } + } catch (final Exception e) { + this.failurePost.incrementAndGet(); + throw e; + } + return response; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + @Override + public Response deleteConfigurationData(final String identifier) { + this.configDelete.incrementAndGet(); + Response response = null; + try { + response = this.delegate.deleteConfigurationData(identifier); + if (response.getStatus() == Status.OK.getStatusCode()) { + this.successDelete.incrementAndGet(); + } else { + this.failureDelete.incrementAndGet(); + } + } catch (final Exception e) { + this.failureDelete.incrementAndGet(); + throw e; + } + return response; + } + + @Override + public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) { + return this.delegate.subscribeToStream(identifier, uriInfo); + } + + @Override + public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) { + return this.delegate.getAvailableStreams(uriInfo); + } + + @Override + public PatchStatusContext patchConfigurationData(final String identifier, final PatchContext payload, + final UriInfo uriInfo) { + return this.delegate.patchConfigurationData(identifier, payload, uriInfo); + } + + @Override + public PatchStatusContext patchConfigurationData(final PatchContext payload, final UriInfo uriInfo) { + return this.delegate.patchConfigurationData(payload, uriInfo); + } + + public BigInteger getConfigDelete() { + return BigInteger.valueOf(this.configDelete.get()); + } + + public BigInteger getConfigGet() { + return BigInteger.valueOf(this.configGet.get()); + } + + public BigInteger getConfigPost() { + return BigInteger.valueOf(this.configPost.get()); + } + + public BigInteger getConfigPut() { + return BigInteger.valueOf(this.configPut.get()); + } + + public BigInteger getOperationalGet() { + return BigInteger.valueOf(this.operationalGet.get()); + } + + public BigInteger getRpc() { + return BigInteger.valueOf(this.rpc.get()); + } + + public BigInteger getSuccessGetConfig() { + return BigInteger.valueOf(this.successGetConfig.get()); + } + + public BigInteger getSuccessGetOperational() { + return BigInteger.valueOf(this.successGetOperational.get()); + } + + public BigInteger getSuccessPost() { + return BigInteger.valueOf(this.successPost.get()); + } + + public BigInteger getSuccessPut() { + return BigInteger.valueOf(this.successPut.get()); + } + + public BigInteger getSuccessDelete() { + return BigInteger.valueOf(this.successDelete.get()); + } + + public BigInteger getFailureGetConfig() { + return BigInteger.valueOf(this.failureGetConfig.get()); + } + + public BigInteger getFailureGetOperational() { + return BigInteger.valueOf(this.failureGetOperational.get()); + } + + public BigInteger getFailurePost() { + return BigInteger.valueOf(this.failurePost.get()); + } + + public BigInteger getFailurePut() { + return BigInteger.valueOf(this.failurePut.get()); + } + + public BigInteger getFailureDelete() { + return BigInteger.valueOf(this.failureDelete.get()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Config.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Config.java new file mode 100644 index 0000000..a115254 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Config.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +public class Config { + private Delete delete; + + private Post post; + + private Get get; + + private Put put; + + public Delete getDelete() { + return delete; + } + + public void setDelete(Delete delete) { + this.delete = delete; + } + + public Post getPost() { + return post; + } + + public void setPost(Post post) { + this.post = post; + } + + public Get getGet() { + return get; + } + + public void setGet(Get get) { + this.get = get; + } + + public Put getPut() { + return put; + } + + public void setPut(Put put) { + this.put = put; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(delete, post, get, put); + + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Config that = (Config) obj; + if (!java.util.Objects.equals(delete, that.delete)) { + return false; + } + + if (!java.util.Objects.equals(post, that.post)) { + return false; + } + + if (!java.util.Objects.equals(get, that.get)) { + return false; + } + + return java.util.Objects.equals(put, that.put); + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Delete.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Delete.java new file mode 100644 index 0000000..16a815b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Delete.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +import java.math.BigInteger; + +public class Delete { + private BigInteger successfulResponses; + + private BigInteger receivedRequests; + + private BigInteger failedResponses; + + public BigInteger getSuccessfulResponses() { + return successfulResponses; + } + + public void setSuccessfulResponses(BigInteger successfulResponses) { + this.successfulResponses = successfulResponses; + } + + public BigInteger getReceivedRequests() { + return receivedRequests; + } + + public void setReceivedRequests(BigInteger receivedRequests) { + this.receivedRequests = receivedRequests; + } + + public BigInteger getFailedResponses() { + return failedResponses; + } + + public void setFailedResponses(BigInteger failedResponses) { + this.failedResponses = failedResponses; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(successfulResponses, receivedRequests, failedResponses); + + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Delete that = (Delete) obj; + if (!java.util.Objects.equals(successfulResponses, that.successfulResponses)) { + return false; + } + + if (!java.util.Objects.equals(receivedRequests, that.receivedRequests)) { + return false; + } + + return java.util.Objects.equals(failedResponses, that.failedResponses); + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Get.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Get.java new file mode 100644 index 0000000..45603a8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Get.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +import java.math.BigInteger; + +public class Get { + private BigInteger successfulResponses; + + private BigInteger receivedRequests; + + private BigInteger failedResponses; + + public BigInteger getSuccessfulResponses() { + return successfulResponses; + } + + public void setSuccessfulResponses(BigInteger successfulResponses) { + this.successfulResponses = successfulResponses; + } + + public BigInteger getReceivedRequests() { + return receivedRequests; + } + + public void setReceivedRequests(BigInteger receivedRequests) { + this.receivedRequests = receivedRequests; + } + + public BigInteger getFailedResponses() { + return failedResponses; + } + + public void setFailedResponses(BigInteger failedResponses) { + this.failedResponses = failedResponses; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(successfulResponses, receivedRequests, failedResponses); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Get that = (Get) obj; + if (!java.util.Objects.equals(successfulResponses, that.successfulResponses)) { + return false; + } + + if (!java.util.Objects.equals(receivedRequests, that.receivedRequests)) { + return false; + } + + return java.util.Objects.equals(failedResponses, that.failedResponses); + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Operational.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Operational.java new file mode 100644 index 0000000..5d9989b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Operational.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +public class Operational { + private Get get; + + public Get getGet() { + return get; + } + + public void setGet(Get get) { + this.get = get; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(get); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Operational that = (Operational) obj; + return java.util.Objects.equals(get, that.get); + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Post.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Post.java new file mode 100644 index 0000000..a466c01 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Post.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +import java.math.BigInteger; + +public class Post { + private BigInteger successfulResponses; + + private BigInteger receivedRequests; + + private BigInteger failedResponses; + + public BigInteger getSuccessfulResponses() { + return successfulResponses; + } + + public void setSuccessfulResponses(BigInteger successfulResponses) { + this.successfulResponses = successfulResponses; + } + + public BigInteger getReceivedRequests() { + return receivedRequests; + } + + public void setReceivedRequests(BigInteger receivedRequests) { + this.receivedRequests = receivedRequests; + } + + public BigInteger getFailedResponses() { + return failedResponses; + } + + public void setFailedResponses(BigInteger failedResponses) { + this.failedResponses = failedResponses; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(successfulResponses, receivedRequests, failedResponses); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Post that = (Post) obj; + if (!java.util.Objects.equals(successfulResponses, that.successfulResponses)) { + return false; + } + + if (!java.util.Objects.equals(receivedRequests, that.receivedRequests)) { + return false; + } + + return java.util.Objects.equals(failedResponses, that.failedResponses); + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Put.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Put.java new file mode 100644 index 0000000..589f02c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Put.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +import java.math.BigInteger; + +public class Put { + private BigInteger successfulResponses; + + private BigInteger receivedRequests; + + private BigInteger failedResponses; + + public BigInteger getSuccessfulResponses() { + return successfulResponses; + } + + public void setSuccessfulResponses(BigInteger successfulResponses) { + this.successfulResponses = successfulResponses; + } + + public BigInteger getReceivedRequests() { + return receivedRequests; + } + + public void setReceivedRequests(BigInteger receivedRequests) { + this.receivedRequests = receivedRequests; + } + + public BigInteger getFailedResponses() { + return failedResponses; + } + + public void setFailedResponses(BigInteger failedResponses) { + this.failedResponses = failedResponses; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(successfulResponses, receivedRequests, failedResponses); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Put that = (Put) obj; + if (!java.util.Objects.equals(successfulResponses, that.successfulResponses)) { + return false; + } + + if (!java.util.Objects.equals(receivedRequests, that.receivedRequests)) { + return false; + } + + return java.util.Objects.equals(failedResponses, that.failedResponses); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/RestConnectorRuntimeMXBean.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/RestConnectorRuntimeMXBean.java new file mode 100644 index 0000000..cafcb8f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/RestConnectorRuntimeMXBean.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +public interface RestConnectorRuntimeMXBean { + Operational getOperational(); + + Rpcs getRpcs(); + + Config getConfig(); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Rpcs.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Rpcs.java new file mode 100644 index 0000000..d849a4d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/Rpcs.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; + +import java.math.BigInteger; + +public class Rpcs { + private BigInteger successfulResponses; + + private BigInteger receivedRequests; + + private BigInteger failedResponses; + + public BigInteger getSuccessfulResponses() { + return successfulResponses; + } + + public void setSuccessfulResponses(BigInteger successfulResponses) { + this.successfulResponses = successfulResponses; + } + + public BigInteger getReceivedRequests() { + return receivedRequests; + } + + public void setReceivedRequests(BigInteger receivedRequests) { + this.receivedRequests = receivedRequests; + } + + public BigInteger getFailedResponses() { + return failedResponses; + } + + public void setFailedResponses(BigInteger failedResponses) { + this.failedResponses = failedResponses; + } + + @Override + public int hashCode() { + return java.util.Objects.hash(successfulResponses, receivedRequests, failedResponses); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Rpcs that = (Rpcs) obj; + if (!java.util.Objects.equals(successfulResponses, that.successfulResponses)) { + return false; + } + + if (!java.util.Objects.equals(receivedRequests, that.receivedRequests)) { + return false; + } + + return java.util.Objects.equals(failedResponses, that.failedResponses); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/package-info.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/package-info.java new file mode 100644 index 0000000..d67ee37 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/jmx/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +/** + * This package contains the statistical JMX classes for the Draft02 restconf implementation. Originally these classes + * were generated by the CSS code generator and were moved to this package on conversion to blueprint. + */ +package org.opendaylight.netconf.sal.restconf.impl.jmx; diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/package-info.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/package-info.java new file mode 100644 index 0000000..e78fddd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/package-info.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl;
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/web/WebInitializer.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/web/WebInitializer.java new file mode 100644 index 0000000..d17c485 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/web/WebInitializer.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 Inocybe Technologies and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.web; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.ServletException; +import org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration; +import org.opendaylight.aaa.filterchain.filters.CustomFilterAdapter; +import org.opendaylight.aaa.web.FilterDetails; +import org.opendaylight.aaa.web.ServletDetails; +import org.opendaylight.aaa.web.WebContext; +import org.opendaylight.aaa.web.WebContextBuilder; +import org.opendaylight.aaa.web.WebContextRegistration; +import org.opendaylight.aaa.web.WebContextSecurer; +import org.opendaylight.aaa.web.WebServer; +import org.opendaylight.aaa.web.servlet.ServletSupport; +import org.opendaylight.netconf.sal.rest.impl.RestconfApplication; + +/** + * Initializes the bierman-02 endpoint. + * + * @author Thomas Pantelis + */ +@Singleton +public class WebInitializer { + + private final WebContextRegistration registration; + + @Inject + public WebInitializer(final WebServer webServer, final WebContextSecurer webContextSecurer, + final ServletSupport servletSupport, final RestconfApplication webApp, + final CustomFilterAdapterConfiguration customFilterAdapterConfig) throws ServletException { + + WebContextBuilder webContextBuilder = WebContext.builder().contextPath("restconf").supportsSessions(false) + .addServlet(ServletDetails.builder().servlet(servletSupport.createHttpServletBuilder(webApp).build()) + .addUrlPattern("/*").build()) + + // Allows user to add javax.servlet.Filter(s) in front of REST services + .addFilter(FilterDetails.builder().filter(new CustomFilterAdapter(customFilterAdapterConfig)) + .addUrlPattern("/*").build()); + + webContextSecurer.requireAuthentication(webContextBuilder, "/*"); + + registration = webServer.registerWebContext(webContextBuilder.build()); + } + + @PreDestroy + public void close() { + if (registration != null) { + registration.close(); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractCommonSubscriber.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractCommonSubscriber.java new file mode 100644 index 0000000..76827d4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractCommonSubscriber.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import com.google.common.eventbus.AsyncEventBus; +import com.google.common.eventbus.EventBus; +import io.netty.channel.Channel; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Features of subscribing part of both notifications. + */ +abstract class AbstractCommonSubscriber extends AbstractQueryParams implements BaseListenerInterface { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCommonSubscriber.class); + + private final Set<Channel> subscribers = ConcurrentHashMap.newKeySet(); + private final EventBus eventBus; + + @SuppressWarnings("rawtypes") + private EventBusChangeRecorder eventBusChangeRecorder; + @SuppressWarnings("rawtypes") + private ListenerRegistration registration; + + /** + * Creating {@link EventBus}. + */ + protected AbstractCommonSubscriber() { + this.eventBus = new AsyncEventBus(Executors.newSingleThreadExecutor()); + } + + @Override + public final boolean hasSubscribers() { + return !this.subscribers.isEmpty(); + } + + @Override + public final Set<Channel> getSubscribers() { + return this.subscribers; + } + + @Override + public final void close() { + if (registration != null) { + this.registration.close(); + this.registration = null; + } + + unregister(); + } + + /** + * Creates event of type {@link EventType#REGISTER}, set {@link Channel} + * subscriber to the event and post event into event bus. + * + * @param subscriber + * Channel + */ + public void addSubscriber(final Channel subscriber) { + if (!subscriber.isActive()) { + LOG.debug("Channel is not active between websocket server and subscriber {}", subscriber.remoteAddress()); + } + final Event event = new Event(EventType.REGISTER); + event.setSubscriber(subscriber); + this.eventBus.post(event); + } + + /** + * Creates event of type {@link EventType#DEREGISTER}, sets {@link Channel} + * subscriber to the event and posts event into event bus. + * + * @param subscriber subscriber channel + */ + public void removeSubscriber(final Channel subscriber) { + LOG.debug("Subscriber {} is removed.", subscriber.remoteAddress()); + final Event event = new Event(EventType.DEREGISTER); + event.setSubscriber(subscriber); + this.eventBus.post(event); + } + + /** + * Sets {@link ListenerRegistration} registration. + * + * @param registration + * DOMDataChangeListener registration + */ + @SuppressWarnings("rawtypes") + public void setRegistration(final ListenerRegistration registration) { + this.registration = registration; + } + + /** + * Checks if {@link ListenerRegistration} registration exist. + * + * @return True if exist, false otherwise. + */ + public boolean isListening() { + return this.registration != null; + } + + /** + * Creating and registering {@link EventBusChangeRecorder} of specific + * listener on {@link EventBus}. + * + * @param listener + * specific listener of notifications + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected <T extends BaseListenerInterface> void register(final T listener) { + this.eventBusChangeRecorder = new EventBusChangeRecorder(listener); + this.eventBus.register(this.eventBusChangeRecorder); + } + + /** + * Post event to event bus. + * + * @param event + * data of incoming notifications + */ + protected void post(final Event event) { + this.eventBus.post(event); + } + + /** + * Removes all subscribers and unregisters event bus change recorder form + * event bus. + */ + protected void unregister() { + this.subscribers.clear(); + this.eventBus.unregister(this.eventBusChangeRecorder); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractNotificationsData.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractNotificationsData.java new file mode 100644 index 0000000..7e7bc1a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractNotificationsData.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Abstract class for processing and preparing data. + * + */ +abstract class AbstractNotificationsData { + private static final Logger LOG = LoggerFactory.getLogger(AbstractNotificationsData.class); + private static final TransformerFactory TF = TransformerFactory.newInstance(); + private static final XMLOutputFactory OF = XMLOutputFactory.newFactory(); + + /** + * Formats data specified by RFC3339. + * + * @param now time stamp + * @return Data specified by RFC3339. + */ + protected static String toRFC3339(final Instant now) { + return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(now, ZoneId.systemDefault())); + } + + /** + * Creates {@link Document} document. + * + * @return {@link Document} document. + */ + protected static Document createDocument() { + return UntrustedXML.newDocumentBuilder().newDocument(); + } + + /** + * Write normalized node to {@link DOMResult}. + * + * @param normalized + * data + * @param inference + * SchemaInferenceStack state for the data + * @return {@link DOMResult} + */ + protected DOMResult writeNormalizedNode(final NormalizedNode normalized, final Inference inference) + throws IOException, XMLStreamException { + final Document doc = UntrustedXML.newDocumentBuilder().newDocument(); + final DOMResult result = new DOMResult(doc); + NormalizedNodeWriter normalizedNodeWriter = null; + NormalizedNodeStreamWriter normalizedNodeStreamWriter = null; + XMLStreamWriter writer = null; + + try { + writer = OF.createXMLStreamWriter(result); + normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, inference); + normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter); + + normalizedNodeWriter.write(normalized); + + normalizedNodeWriter.flush(); + } finally { + if (normalizedNodeWriter != null) { + normalizedNodeWriter.close(); + } + if (normalizedNodeStreamWriter != null) { + normalizedNodeStreamWriter.close(); + } + if (writer != null) { + writer.close(); + } + } + + return result; + } + + /** + * Generating base element of every notification. + * + * @param doc + * base {@link Document} + * @return element of {@link Document} + */ + protected Element basePartDoc(final Document doc) { + final Element notificationElement = + doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0", "notification"); + + doc.appendChild(notificationElement); + + final Element eventTimeElement = doc.createElement("eventTime"); + eventTimeElement.setTextContent(toRFC3339(Instant.now())); + notificationElement.appendChild(eventTimeElement); + + return notificationElement; + } + + /** + * Generating of {@link Document} transforming to string. + * + * @param doc + * {@link Document} with data + * @return - string from {@link Document} + */ + protected String transformDoc(final Document doc) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + final Transformer transformer = TF.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(new DOMSource(doc), new StreamResult(out)); + } catch (final TransformerException e) { + // FIXME: this should raise an exception + final String msg = "Error during transformation of Document into String"; + LOG.error(msg, e); + return msg; + } + + return new String(out.toByteArray(), StandardCharsets.UTF_8); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java new file mode 100644 index 0000000..4697646 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/AbstractQueryParams.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; +import java.io.StringReader; +import java.time.Instant; +import java.util.Optional; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +/** + * Features of query parameters part of both notifications. + * + */ +abstract class AbstractQueryParams extends AbstractNotificationsData { + // FIXME: BUG-7956: switch to using UntrustedXML + private static final DocumentBuilderFactory DBF; + + static { + final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); + f.setCoalescing(true); + f.setExpandEntityReferences(false); + f.setIgnoringElementContentWhitespace(true); + f.setIgnoringComments(true); + f.setXIncludeAware(false); + try { + f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + f.setFeature("http://xml.org/sax/features/external-general-entities", false); + f.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (final ParserConfigurationException e) { + throw new ExceptionInInitializerError(e); + } + DBF = f; + } + + // FIXME: these should be final + private Instant start = null; + private Instant stop = null; + private String filter = null; + private boolean leafNodesOnly = false; + private boolean skipNotificationData = false; + + @VisibleForTesting + public final Instant getStart() { + return start; + } + + /** + * Set query parameters for listener. + * + * @param start + * start-time of getting notification + * @param stop + * stop-time of getting notification + * @param filter + * indicate which subset of all possible events are of interest + * @param leafNodesOnly + * if true, notifications will contain changes to leaf nodes only + * @param skipNotificationData + * if true, notification will not contain changed data + */ + @SuppressWarnings("checkstyle:hiddenField") + public void setQueryParams(final Instant start, final Optional<Instant> stop, final Optional<String> filter, + final boolean leafNodesOnly, final boolean skipNotificationData) { + this.start = requireNonNull(start); + this.stop = stop.orElse(null); + this.filter = filter.orElse(null); + this.leafNodesOnly = leafNodesOnly; + this.skipNotificationData = skipNotificationData; + } + + /** + * Check whether this query should only notify about leaf node changes. + * + * @return true if this query should only notify about leaf node changes + */ + public boolean getLeafNodesOnly() { + return leafNodesOnly; + } + + /** + * Check whether this query should notify changes without data. + * + * @return true if this query should notify about changes with data + */ + public boolean isSkipNotificationData() { + return skipNotificationData; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + <T extends BaseListenerInterface> boolean checkStartStop(final Instant now, final T listener) { + if (this.stop != null) { + if (this.start.compareTo(now) < 0 && this.stop.compareTo(now) > 0) { + return true; + } + if (this.stop.compareTo(now) < 0) { + try { + listener.close(); + } catch (final Exception e) { + throw new RestconfDocumentedException("Problem with unregister listener." + e); + } + } + } else if (this.start != null) { + if (this.start.compareTo(now) < 0) { + this.start = null; + return true; + } + } else { + return true; + } + return false; + } + + /** + * Check if is filter used and then prepare and post data do client. + * + * @param xml data of notification + */ + @SuppressWarnings("checkstyle:IllegalCatch") + boolean checkFilter(final String xml) { + if (this.filter == null) { + return true; + } + + try { + return parseFilterParam(xml); + } catch (final Exception e) { + throw new RestconfDocumentedException("Problem while parsing filter.", e); + } + } + + /** + * Parse and evaluate filter value by xml. + * + * @return true or false - depends on filter expression and data of + * notifiaction + * @throws Exception if operation fails + */ + private boolean parseFilterParam(final String xml) throws Exception { + final Document docOfXml = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); + final XPath xPath = XPathFactory.newInstance().newXPath(); + // FIXME: BUG-7956: xPath.setNamespaceContext(nsContext); + return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/BaseListenerInterface.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/BaseListenerInterface.java new file mode 100644 index 0000000..4804e16 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/BaseListenerInterface.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import io.netty.channel.Channel; +import java.util.Set; + +/** + * Base interface for both listeners({@link ListenerAdapter}, + * {@link NotificationListenerAdapter}). + */ +interface BaseListenerInterface extends AutoCloseable { + + /** + * Return all subscribers of listener. + * + * @return set of subscribers + */ + Set<Channel> getSubscribers(); + + /** + * Checks if exists at least one {@link Channel} subscriber. + * + * @return True if exist at least one {@link Channel} subscriber, false + * otherwise. + */ + boolean hasSubscribers(); + + /** + * Get name of stream. + * + * @return stream name + */ + String getStreamName(); + + /** + * Get output type. + * + * @return outputType + */ + String getOutputType(); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Event.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Event.java new file mode 100644 index 0000000..486d807 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Event.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import io.netty.channel.Channel; + +/** + * Represents event of specific {@link EventType} type, holds data and + * {@link Channel} subscriber. + */ +class Event { + private final EventType type; + private Channel subscriber; + private String data; + + /** + * Creates new event specified by {@link EventType} type. + * + * @param type + * EventType + */ + Event(final EventType type) { + this.type = type; + } + + /** + * Gets the {@link Channel} subscriber. + * + * @return Channel + */ + public Channel getSubscriber() { + return this.subscriber; + } + + /** + * Sets subscriber for event. + * + * @param subscriber + * Channel + */ + public void setSubscriber(final Channel subscriber) { + this.subscriber = subscriber; + } + + /** + * Gets event String. + * + * @return String representation of event data. + */ + public String getData() { + return this.data; + } + + /** + * Sets event data. + * + * @param data + * String. + */ + public void setData(final String data) { + this.data = data; + } + + /** + * Gets event type. + * + * @return The type of the event. + */ + public EventType getType() { + return this.type; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventBusChangeRecorder.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventBusChangeRecorder.java new file mode 100644 index 0000000..11e5656 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventBusChangeRecorder.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import com.google.common.eventbus.Subscribe; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class EventBusChangeRecorder<T extends BaseListenerInterface> { + + private static final Logger LOG = LoggerFactory.getLogger(EventBusChangeRecorder.class); + private final T listener; + + /** + * Event bus change recorder of specific listener of notifications. + * + * @param listener + * specific listener + */ + EventBusChangeRecorder(final T listener) { + this.listener = listener; + } + + @Subscribe + public void recordCustomerChange(final Event event) { + if (event.getType() == EventType.REGISTER) { + final Channel subscriber = event.getSubscriber(); + if (!this.listener.getSubscribers().contains(subscriber)) { + this.listener.getSubscribers().add(subscriber); + } + } else if (event.getType() == EventType.DEREGISTER) { + this.listener.getSubscribers().remove(event.getSubscriber()); + Notificator.removeListenerIfNoSubscriberExists(this.listener); + } else if (event.getType() == EventType.NOTIFY) { + for (final Channel subscriber : this.listener.getSubscribers()) { + if (subscriber.isActive()) { + LOG.debug("Data are sent to subscriber {}:", subscriber.remoteAddress()); + subscriber.writeAndFlush(new TextWebSocketFrame(event.getData())); + } else { + LOG.debug("Subscriber {} is removed - channel is not active yet.", subscriber.remoteAddress()); + this.listener.getSubscribers().remove(subscriber); + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventType.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventType.java new file mode 100644 index 0000000..ba7c2a3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/EventType.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +/** + * Type of the event. + */ +enum EventType { + REGISTER, DEREGISTER, NOTIFY +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java new file mode 100644 index 0000000..fc85862 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2014, 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.io.IOException; +import java.time.Instant; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.dom.DOMResult; +import org.json.XML; +import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * {@link ListenerAdapter} is responsible to track events, which occurred by + * changing data in data source. + */ +public class ListenerAdapter extends AbstractCommonSubscriber implements ClusteredDOMDataTreeChangeListener { + + private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapter.class); + private static final String DATA_CHANGE_EVENT = "data-change-event"; + private static final String PATH = "path"; + private static final String OPERATION = "operation"; + + private final ControllerContext controllerContext; + private final YangInstanceIdentifier path; + private final String streamName; + private final NotificationOutputType outputType; + + /** + * Creates new {@link ListenerAdapter} listener specified by path and stream + * name and register for subscribing. + * + * @param path + * Path to data in data store. + * @param streamName + * The name of the stream. + * @param outputType + * Type of output on notification (JSON, XML) + */ + @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "non-final for testing") + ListenerAdapter(final YangInstanceIdentifier path, final String streamName, + final NotificationOutputType outputType, final ControllerContext controllerContext) { + this.outputType = requireNonNull(outputType); + this.path = requireNonNull(path); + checkArgument(streamName != null && !streamName.isEmpty()); + this.streamName = streamName; + this.controllerContext = controllerContext; + register(this); + } + + @Override + public void onInitialData() { + // No-op + } + + @Override + public void onDataTreeChanged(final List<DataTreeCandidate> dataTreeCandidates) { + final Instant now = Instant.now(); + if (!checkStartStop(now, this)) { + return; + } + + final String xml = prepareXml(dataTreeCandidates); + if (checkFilter(xml)) { + prepareAndPostData(xml); + } + } + + /** + * Gets the name of the stream. + * + * @return The name of the stream. + */ + @Override + public String getStreamName() { + return streamName; + } + + @Override + public String getOutputType() { + return outputType.getName(); + } + + /** + * Get path pointed to data in data store. + * + * @return Path pointed to data in data store. + */ + public YangInstanceIdentifier getPath() { + return path; + } + + /** + * Prepare data of notification and data to client. + * + * @param xml data + */ + private void prepareAndPostData(final String xml) { + final Event event = new Event(EventType.NOTIFY); + if (outputType.equals(NotificationOutputType.JSON)) { + event.setData(XML.toJSONObject(xml).toString()); + } else { + event.setData(xml); + } + post(event); + } + + /** + * Tracks events of data change by customer. + */ + + /** + * Prepare data in printable form and transform it to String. + * + * @return Data in printable form. + */ + private String prepareXml(final Collection<DataTreeCandidate> candidates) { + final EffectiveModelContext schemaContext = controllerContext.getGlobalSchema(); + final DataSchemaContextTree dataContextTree = DataSchemaContextTree.from(schemaContext); + final Document doc = createDocument(); + final Element notificationElement = basePartDoc(doc); + + final Element dataChangedNotificationEventElement = doc.createElementNS( + "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification"); + + addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, candidates, + schemaContext, dataContextTree); + notificationElement.appendChild(dataChangedNotificationEventElement); + return transformDoc(doc); + } + + /** + * Adds values to data changed notification event element. + * + * @param doc + * {@link Document} + * @param dataChangedNotificationEventElement + * {@link Element} + * @param dataTreeCandidates + * {@link DataTreeCandidate} + */ + private void addValuesToDataChangedNotificationEventElement(final Document doc, + final Element dataChangedNotificationEventElement, + final Collection<DataTreeCandidate> dataTreeCandidates, + final EffectiveModelContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) { + + for (DataTreeCandidate dataTreeCandidate : dataTreeCandidates) { + DataTreeCandidateNode candidateNode = dataTreeCandidate.getRootNode(); + if (candidateNode == null) { + continue; + } + YangInstanceIdentifier yiid = dataTreeCandidate.getRootPath(); + + boolean isSkipNotificationData = this.isSkipNotificationData(); + if (isSkipNotificationData) { + createCreatedChangedDataChangeEventElementWithoutData(doc, + dataChangedNotificationEventElement, dataTreeCandidate.getRootNode()); + } else { + addNodeToDataChangeNotificationEventElement(doc, dataChangedNotificationEventElement, candidateNode, + yiid.getParent(), schemaContext, dataSchemaContextTree); + } + } + } + + private void addNodeToDataChangeNotificationEventElement(final Document doc, + final Element dataChangedNotificationEventElement, final DataTreeCandidateNode candidateNode, + final YangInstanceIdentifier parentYiid, final EffectiveModelContext schemaContext, + final DataSchemaContextTree dataSchemaContextTree) { + + Optional<NormalizedNode> optionalNormalizedNode = Optional.empty(); + switch (candidateNode.getModificationType()) { + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + optionalNormalizedNode = candidateNode.getDataAfter(); + break; + case DELETE: + case DISAPPEARED: + optionalNormalizedNode = candidateNode.getDataBefore(); + break; + case UNMODIFIED: + default: + break; + } + + if (optionalNormalizedNode.isEmpty()) { + LOG.error("No node present in notification for {}", candidateNode); + return; + } + + NormalizedNode normalizedNode = optionalNormalizedNode.get(); + YangInstanceIdentifier yiid = YangInstanceIdentifier.builder(parentYiid) + .append(normalizedNode.getIdentifier()).build(); + + boolean isNodeMixin = controllerContext.isNodeMixin(yiid); + boolean isSkippedNonLeaf = getLeafNodesOnly() && !(normalizedNode instanceof LeafNode); + if (!isNodeMixin && !isSkippedNonLeaf) { + Node node = null; + switch (candidateNode.getModificationType()) { + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + Operation op = candidateNode.getDataBefore().isPresent() ? Operation.UPDATED : Operation.CREATED; + node = createCreatedChangedDataChangeEventElement(doc, yiid, normalizedNode, op, + schemaContext, dataSchemaContextTree); + break; + case DELETE: + case DISAPPEARED: + node = createDataChangeEventElement(doc, yiid, Operation.DELETED); + break; + case UNMODIFIED: + default: + break; + } + if (node != null) { + dataChangedNotificationEventElement.appendChild(node); + } + } + + for (DataTreeCandidateNode childNode : candidateNode.getChildNodes()) { + addNodeToDataChangeNotificationEventElement(doc, dataChangedNotificationEventElement, childNode, + yiid, schemaContext, dataSchemaContextTree); + } + } + + /** + * Creates changed event element from data. + * + * @param doc + * {@link Document} + * @param dataPath + * Path to data in data store. + * @param operation + * {@link Operation} + * @return {@link Node} node represented by changed event element. + */ + private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier dataPath, + final Operation operation) { + final Element dataChangeEventElement = doc.createElement(DATA_CHANGE_EVENT); + final Element pathElement = doc.createElement(PATH); + addPathAsValueToElement(dataPath, pathElement); + dataChangeEventElement.appendChild(pathElement); + + final Element operationElement = doc.createElement(OPERATION); + operationElement.setTextContent(operation.value); + dataChangeEventElement.appendChild(operationElement); + + return dataChangeEventElement; + } + + /** + * Creates data change notification element without data element. + * + * @param doc + * {@link Document} + * @param dataChangedNotificationEventElement + * {@link Element} + * @param candidateNode + * {@link DataTreeCandidateNode} + */ + private void createCreatedChangedDataChangeEventElementWithoutData(final Document doc, + final Element dataChangedNotificationEventElement, final DataTreeCandidateNode candidateNode) { + final Operation operation; + switch (candidateNode.getModificationType()) { + case APPEARED: + case SUBTREE_MODIFIED: + case WRITE: + operation = candidateNode.getDataBefore().isPresent() ? Operation.UPDATED : Operation.CREATED; + break; + case DELETE: + case DISAPPEARED: + operation = Operation.DELETED; + break; + case UNMODIFIED: + default: + return; + } + Node dataChangeEventElement = createDataChangeEventElement(doc, getPath(), operation); + dataChangedNotificationEventElement.appendChild(dataChangeEventElement); + + } + + private Node createCreatedChangedDataChangeEventElement(final Document doc, + final YangInstanceIdentifier eventPath, final NormalizedNode normalized, final Operation operation, + final EffectiveModelContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) { + final Element dataChangeEventElement = doc.createElement(DATA_CHANGE_EVENT); + final Element pathElement = doc.createElement(PATH); + addPathAsValueToElement(eventPath, pathElement); + dataChangeEventElement.appendChild(pathElement); + + final Element operationElement = doc.createElement(OPERATION); + operationElement.setTextContent(operation.value); + dataChangeEventElement.appendChild(operationElement); + + final SchemaInferenceStack stack = dataSchemaContextTree.enterPath(eventPath).orElseThrow().stack(); + if (!(normalized instanceof MapEntryNode) && !(normalized instanceof UnkeyedListEntryNode) + && !stack.isEmpty()) { + stack.exit(); + } + + final var inference = stack.toInference(); + + try { + final DOMResult domResult = writeNormalizedNode(normalized, inference); + final Node result = doc.importNode(domResult.getNode().getFirstChild(), true); + final Element dataElement = doc.createElement("data"); + dataElement.appendChild(result); + dataChangeEventElement.appendChild(dataElement); + } catch (final IOException e) { + LOG.error("Error in writer ", e); + } catch (final XMLStreamException e) { + LOG.error("Error processing stream", e); + } + + return dataChangeEventElement; + } + + /** + * Adds path as value to element. + * + * @param dataPath + * Path to data in data store. + * @param element + * {@link Element} + */ + @SuppressWarnings("rawtypes") + private void addPathAsValueToElement(final YangInstanceIdentifier dataPath, final Element element) { + final YangInstanceIdentifier normalizedPath = controllerContext.toXpathRepresentation(dataPath); + final StringBuilder textContent = new StringBuilder(); + + for (final PathArgument pathArgument : normalizedPath.getPathArguments()) { + if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) { + continue; + } + textContent.append("/"); + writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType()); + if (pathArgument instanceof NodeIdentifierWithPredicates) { + for (final Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) pathArgument).entrySet()) { + final QName keyValue = entry.getKey(); + final String predicateValue = String.valueOf(entry.getValue()); + textContent.append("["); + writeIdentifierWithNamespacePrefix(element, textContent, keyValue); + textContent.append("='"); + textContent.append(predicateValue); + textContent.append("'"); + textContent.append("]"); + } + } else if (pathArgument instanceof NodeWithValue) { + textContent.append("[.='"); + textContent.append(((NodeWithValue) pathArgument).getValue()); + textContent.append("'"); + textContent.append("]"); + } + } + element.setTextContent(textContent.toString()); + } + + /** + * Writes identifier that consists of prefix and QName. + * + * @param element + * {@link Element} + * @param textContent + * StringBuilder + * @param qualifiedName + * QName + */ + private void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent, + final QName qualifiedName) { + final Module module = controllerContext.getGlobalSchema().findModule(qualifiedName.getModule()) + .get(); + + textContent.append(module.getName()); + textContent.append(":"); + textContent.append(qualifiedName.getLocalName()); + } + + /** + * Consists of three types {@link Operation#CREATED}, + * {@link Operation#UPDATED} and {@link Operation#DELETED}. + */ + private enum Operation { + CREATED("created"), UPDATED("updated"), DELETED("deleted"); + + private final String value; + + Operation(final String value) { + this.value = value; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java new file mode 100644 index 0000000..3061285 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.time.Instant; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.dom.DOMResult; +import org.opendaylight.mdsal.dom.api.DOMNotification; +import org.opendaylight.mdsal.dom.api.DOMNotificationListener; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier; +import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * {@link NotificationListenerAdapter} is responsible to track events on notifications. + */ +public final class NotificationListenerAdapter extends AbstractCommonSubscriber implements DOMNotificationListener { + private static final Logger LOG = LoggerFactory.getLogger(NotificationListenerAdapter.class); + + private final ControllerContext controllerContext; + private final String streamName; + private final Absolute path; + private final String outputType; + + /** + * Set path of listener and stream name, register event bus. + * + * @param path + * path of notification + * @param streamName + * stream name of listener + * @param outputType + * type of output on notification (JSON, XML) + */ + NotificationListenerAdapter(final Absolute path, final String streamName, final String outputType, + final ControllerContext controllerContext) { + register(this); + this.outputType = requireNonNull(outputType); + this.path = requireNonNull(path); + checkArgument(streamName != null && !streamName.isEmpty()); + this.streamName = streamName; + this.controllerContext = controllerContext; + } + + /** + * Get outputType of listener. + * + * @return the outputType + */ + @Override + public String getOutputType() { + return outputType; + } + + @Override + public void onNotification(final DOMNotification notification) { + final Instant now = Instant.now(); + if (!checkStartStop(now, this)) { + return; + } + + final EffectiveModelContext schemaContext = controllerContext.getGlobalSchema(); + final String xml = prepareXml(schemaContext, notification); + if (checkFilter(xml)) { + prepareAndPostData(outputType.equals("JSON") ? prepareJson(schemaContext, notification) : xml); + } + } + + /** + * Get stream name of this listener. + * + * @return {@link String} + */ + @Override + public String getStreamName() { + return streamName; + } + + /** + * Get schema path of notification. + * + * @return {@link Absolute} SchemaNodeIdentifier + */ + public Absolute getSchemaPath() { + return path; + } + + /** + * Prepare data of notification and data to client. + * + * @param data data + */ + private void prepareAndPostData(final String data) { + final Event event = new Event(EventType.NOTIFY); + event.setData(data); + post(event); + } + + /** + * Prepare json from notification data. + * + * @return json as {@link String} + */ + @VisibleForTesting + String prepareJson(final EffectiveModelContext schemaContext, final DOMNotification notification) { + final JsonObject json = new JsonObject(); + json.add("ietf-restconf:notification", JsonParser.parseString(writeBodyToString(schemaContext, notification))); + json.addProperty("event-time", ListenerAdapter.toRFC3339(Instant.now())); + return json.toString(); + } + + private static String writeBodyToString(final EffectiveModelContext schemaContext, + final DOMNotification notification) { + final Writer writer = new StringWriter(); + final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.createExclusiveWriter( + JSONCodecFactorySupplier.DRAFT_LHOTKA_NETMOD_YANG_JSON_02.getShared(schemaContext), + notification.getType(), null, JsonWriterFactory.createJsonWriter(writer)); + final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream); + try { + nodeWriter.write(notification.getBody()); + nodeWriter.close(); + } catch (final IOException e) { + throw new RestconfDocumentedException("Problem while writing body of notification to JSON. ", e); + } + return writer.toString(); + } + + private String prepareXml(final EffectiveModelContext schemaContext, final DOMNotification notification) { + final Document doc = createDocument(); + final Element notificationElement = basePartDoc(doc); + + final Element notificationEventElement = doc.createElementNS( + "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "create-notification-stream"); + addValuesToNotificationEventElement(doc, notificationEventElement, schemaContext, notification); + notificationElement.appendChild(notificationEventElement); + + return transformDoc(doc); + } + + private void addValuesToNotificationEventElement(final Document doc, final Element element, + final EffectiveModelContext schemaContext, final DOMNotification notification) { + try { + final DOMResult domResult = writeNormalizedNode(notification.getBody(), + SchemaInferenceStack.of(schemaContext, path).toInference()); + final Node result = doc.importNode(domResult.getNode().getFirstChild(), true); + final Element dataElement = doc.createElement("notification"); + dataElement.appendChild(result); + element.appendChild(dataElement); + } catch (final IOException e) { + LOG.error("Error in writer ", e); + } catch (final XMLStreamException e) { + LOG.error("Error processing stream", e); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Notificator.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Notificator.java new file mode 100644 index 0000000..5bfa79f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Notificator.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link Notificator} is responsible to create, remove and find + * {@link ListenerAdapter} listener. + */ +public final class Notificator { + + private static Map<String, ListenerAdapter> dataChangeListener = new ConcurrentHashMap<>(); + private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName = + new ConcurrentHashMap<>(); + + private static final Logger LOG = LoggerFactory.getLogger(Notificator.class); + private static final Lock LOCK = new ReentrantLock(); + + private Notificator() { + } + + /** + * Returns list of all stream names. + */ + public static Set<String> getStreamNames() { + return dataChangeListener.keySet(); + } + + /** + * Gets {@link ListenerAdapter} specified by stream name. + * + * @param streamName + * The name of the stream. + * @return {@link ListenerAdapter} specified by stream name. + */ + public static ListenerAdapter getListenerFor(final String streamName) { + return dataChangeListener.get(streamName); + } + + /** + * Checks if the listener specified by {@link YangInstanceIdentifier} path exist. + * + * @param streamName name of the stream + * @return True if the listener exist, false otherwise. + */ + public static boolean existListenerFor(final String streamName) { + return dataChangeListener.containsKey(streamName); + } + + /** + * Creates new {@link ListenerAdapter} listener from + * {@link YangInstanceIdentifier} path and stream name. + * + * @param path + * Path to data in data repository. + * @param streamName + * The name of the stream. + * @param outputType + * Spcific type of output for notifications - XML or JSON + * @return New {@link ListenerAdapter} listener from + * {@link YangInstanceIdentifier} path and stream name. + */ + public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName, + final NotificationOutputType outputType, final ControllerContext controllerContext) { + final ListenerAdapter listener = new ListenerAdapter(path, streamName, outputType, controllerContext); + try { + LOCK.lock(); + dataChangeListener.put(streamName, listener); + } finally { + LOCK.unlock(); + } + return listener; + } + + /** + * Looks for listener determined by {@link YangInstanceIdentifier} path and removes it. + * Creates String representation of stream name from URI. Removes slash from URI in start and end position. + * + * @param uri + * URI for creation stream name. + * @return String representation of stream name. + */ + public static String createStreamNameFromUri(final String uri) { + if (uri == null) { + return null; + } + String result = uri; + if (result.startsWith("/")) { + result = result.substring(1); + } + if (result.endsWith("/")) { + result = result.substring(0, result.length() - 1); + } + return result; + } + + /** + * Removes all listeners. + */ + @SuppressWarnings("checkstyle:IllegalCatch") + public static void removeAllListeners() { + for (final ListenerAdapter listener : dataChangeListener.values()) { + try { + listener.close(); + } catch (final Exception e) { + LOG.error("Failed to close listener", e); + } + } + try { + LOCK.lock(); + dataChangeListener = new ConcurrentHashMap<>(); + } finally { + LOCK.unlock(); + } + } + + /** + * Delete {@link ListenerAdapter} listener specified in parameter. + * + * @param <T> + * + * @param listener + * ListenerAdapter + */ + @SuppressWarnings("checkstyle:IllegalCatch") + private static <T extends BaseListenerInterface> void deleteListener(final T listener) { + if (listener != null) { + try { + listener.close(); + } catch (final Exception e) { + LOG.error("Failed to close listener", e); + } + try { + LOCK.lock(); + dataChangeListener.remove(listener.getStreamName()); + } finally { + LOCK.unlock(); + } + } + } + + /** + * Check if the listener specified by qnames of request exist. + * + * @param streamName + * name of stream + * @return True if the listener exist, false otherwise. + */ + public static boolean existNotificationListenerFor(final String streamName) { + return notificationListenersByStreamName.containsKey(streamName); + } + + /** + * Prepare listener for notification ({@link NotificationDefinition}). + * + * @param paths + * paths of notifications + * @param streamName + * name of stream (generated by paths) + * @param outputType + * type of output for onNotification - XML or JSON + * @return List of {@link NotificationListenerAdapter} by paths + */ + public static List<NotificationListenerAdapter> createNotificationListener(final List<Absolute> paths, + final String streamName, final String outputType, final ControllerContext controllerContext) { + final List<NotificationListenerAdapter> listListeners = new ArrayList<>(); + for (final Absolute path : paths) { + final NotificationListenerAdapter listener = + new NotificationListenerAdapter(path, streamName, outputType, controllerContext); + listListeners.add(listener); + } + try { + LOCK.lock(); + notificationListenersByStreamName.put(streamName, listListeners); + } finally { + LOCK.unlock(); + } + return listListeners; + } + + public static <T extends BaseListenerInterface> void removeListenerIfNoSubscriberExists(final T listener) { + if (!listener.hasSubscribers()) { + if (listener instanceof NotificationListenerAdapter) { + deleteNotificationListener(listener); + } else { + deleteListener(listener); + } + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private static <T extends BaseListenerInterface> void deleteNotificationListener(final T listener) { + if (listener != null) { + try { + listener.close(); + } catch (final Exception e) { + LOG.error("Failed to close listener", e); + } + try { + LOCK.lock(); + notificationListenersByStreamName.remove(listener.getStreamName()); + } finally { + LOCK.unlock(); + } + } + } + + public static List<NotificationListenerAdapter> getNotificationListenerFor(final String streamName) { + return notificationListenersByStreamName.get(streamName); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServer.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServer.java new file mode 100644 index 0000000..a295c54 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServer.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.websockets; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link WebSocketServer} is the singleton responsible for starting and stopping the + * web socket server. + */ +public final class WebSocketServer implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(WebSocketServer.class); + + private static final String DEFAULT_ADDRESS = "0.0.0.0"; + + private static WebSocketServer instance = null; + + private final String address; + private final int port; + + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + + private WebSocketServer(final String address, final int port) { + this.address = address; + this.port = port; + } + + /** + * Create singleton instance of {@link WebSocketServer}. + * + * @param port TCP port used for this server + * @return instance of {@link WebSocketServer} + */ + private static WebSocketServer createInstance(final int port) { + instance = createInstance(DEFAULT_ADDRESS, port); + return instance; + } + + public static WebSocketServer createInstance(final String address, final int port) { + checkState(instance == null, "createInstance() has already been called"); + checkArgument(port >= 1024, "Privileged port (below 1024) is not allowed"); + + instance = new WebSocketServer(requireNonNull(address, "Address cannot be null."), port); + LOG.info("Created WebSocketServer on {}:{}", address, port); + return instance; + } + + /** + * Get the websocket of TCP port. + * + * @return websocket TCP port + */ + public int getPort() { + return port; + } + + /** + * Get instance of {@link WebSocketServer} created by {@link #createInstance(int)}. + * + * @return instance of {@link WebSocketServer} + */ + public static WebSocketServer getInstance() { + return requireNonNull(instance, "createInstance() must be called prior to getInstance()"); + } + + /** + * Get instance of {@link WebSocketServer} created by {@link #createInstance(int)}. + * If an instance doesnt exist create one with the provided fallback port. + * + * @return instance of {@link WebSocketServer} + */ + public static WebSocketServer getInstance(final int fallbackPort) { + if (instance != null) { + return instance; + } + + LOG.warn("No instance for WebSocketServer found, creating one with a fallback port: {}", fallbackPort); + return createInstance(fallbackPort); + } + + /** + * Destroy the existing instance. + */ + public static void destroyInstance() { + checkState(instance != null, "createInstance() must be called prior to destroyInstance()"); + + instance.stop(); + instance = null; + LOG.info("Destroyed WebSocketServer."); + } + + @Override + @SuppressWarnings("checkstyle:IllegalCatch") + public void run() { + bossGroup = new NioEventLoopGroup(); + workerGroup = new NioEventLoopGroup(); + try { + final ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) + .childHandler(new WebSocketServerInitializer()); + + final Channel channel = serverBootstrap.bind(address, port).sync().channel(); + LOG.info("Web socket server started at address {}, port {}.", address, port); + + channel.closeFuture().sync(); + } catch (final InterruptedException e) { + LOG.error("Web socket server encountered an error during startup attempt on port {}", port, e); + } catch (Throwable throwable) { + // sync() re-throws exceptions declared as Throwable, so the compiler doesn't see them + LOG.error("Error while binding to address {}, port {}", address, port, throwable); + throw throwable; + } finally { + stop(); + } + } + + /** + * Stops the web socket server and removes all listeners. + */ + private void stop() { + LOG.info("Stopping the web socket server instance on port {}", port); + Notificator.removeAllListeners(); + if (bossGroup != null) { + bossGroup.shutdownGracefully(); + bossGroup = null; + } + if (workerGroup != null) { + workerGroup.shutdownGracefully(); + workerGroup = null; + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerHandler.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerHandler.java new file mode 100644 index 0000000..ed90e3f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerHandler.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.websockets; + +import static io.netty.handler.codec.http.HttpHeaderNames.HOST; +import static io.netty.handler.codec.http.HttpMethod.GET; +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; +import static io.netty.handler.codec.http.HttpUtil.setContentLength; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; +import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; +import io.netty.util.CharsetUtil; +import java.util.List; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link WebSocketServerHandler} is implementation of {@link SimpleChannelInboundHandler} which allow handle + * {@link FullHttpRequest} and {@link WebSocketFrame} messages. + */ +public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> { + private static final Logger LOG = LoggerFactory.getLogger(WebSocketServerHandler.class); + + private WebSocketServerHandshaker handshaker; + + @Override + protected void channelRead0(final ChannelHandlerContext ctx, final Object msg) { + if (msg instanceof FullHttpRequest) { + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + handleWebSocketFrame(ctx, (WebSocketFrame) msg); + } + } + + /** + * Checks if HTTP request method is GET and if is possible to decode HTTP result of request. + * + * @param ctx ChannelHandlerContext + * @param req FullHttpRequest + */ + private void handleHttpRequest(final ChannelHandlerContext ctx, final FullHttpRequest req) { + // Handle a bad request. + if (!req.decoderResult().isSuccess()) { + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST)); + return; + } + + // Allow only GET methods. + if (req.method() != GET) { + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); + return; + } + + final String streamName = Notificator.createStreamNameFromUri(req.uri()); + if (streamName.contains(RestconfImpl.DATA_SUBSCR)) { + final ListenerAdapter listener = Notificator.getListenerFor(streamName); + if (listener != null) { + listener.addSubscriber(ctx.channel()); + LOG.debug("Subscriber successfully registered."); + } else { + LOG.error("Listener for stream with name '{}' was not found.", streamName); + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR)); + } + } else if (streamName.contains(RestconfImpl.NOTIFICATION_STREAM)) { + final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName); + if (listeners != null && !listeners.isEmpty()) { + for (final NotificationListenerAdapter listener : listeners) { + listener.addSubscriber(ctx.channel()); + LOG.debug("Subscriber successfully registered."); + } + } else { + LOG.error("Listener for stream with name '{}' was not found.", streamName); + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR)); + } + } + + // Handshake + final WebSocketServerHandshakerFactory wsFactory = + new WebSocketServerHandshakerFactory(getWebSocketLocation(req), + null, false); + this.handshaker = wsFactory.newHandshaker(req); + if (this.handshaker == null) { + WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); + } else { + this.handshaker.handshake(ctx.channel(), req); + } + } + + /** + * Checks response status, send response and close connection if necessary. + * + * @param ctx ChannelHandlerContext + * @param req HttpRequest + * @param res FullHttpResponse + */ + private static void sendHttpResponse(final ChannelHandlerContext ctx, final HttpRequest req, + final FullHttpResponse res) { + // Generate an error page if response getStatus code is not OK (200). + final boolean notOkay = !OK.equals(res.status()); + if (notOkay) { + res.content().writeCharSequence(res.status().toString(), CharsetUtil.UTF_8); + setContentLength(res, res.content().readableBytes()); + } + + // Send the response and close the connection if necessary. + final ChannelFuture f = ctx.channel().writeAndFlush(res); + if (notOkay || !isKeepAlive(req)) { + f.addListener(ChannelFutureListener.CLOSE); + } + } + + /** + * Handles web socket frame. + * + * @param ctx {@link ChannelHandlerContext} + * @param frame {@link WebSocketFrame} + */ + private void handleWebSocketFrame(final ChannelHandlerContext ctx, final WebSocketFrame frame) { + if (frame instanceof CloseWebSocketFrame) { + this.handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); + final String streamName = Notificator.createStreamNameFromUri(((CloseWebSocketFrame) frame).reasonText()); + if (streamName.contains(RestconfImpl.DATA_SUBSCR)) { + final ListenerAdapter listener = Notificator.getListenerFor(streamName); + if (listener != null) { + listener.removeSubscriber(ctx.channel()); + LOG.debug("Subscriber successfully registered."); + + Notificator.removeListenerIfNoSubscriberExists(listener); + } + } else if (streamName.contains(RestconfImpl.NOTIFICATION_STREAM)) { + final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName); + if (listeners != null && !listeners.isEmpty()) { + for (final NotificationListenerAdapter listener : listeners) { + listener.removeSubscriber(ctx.channel()); + } + } + } + return; + } else if (frame instanceof PingWebSocketFrame) { + ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); + return; + } + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { + ctx.close(); + } + + /** + * Get web socket location from HTTP request. + * + * @param req HTTP request from which the location will be returned + * @return String representation of web socket location. + */ + private static String getWebSocketLocation(final HttpRequest req) { + return "ws://" + req.headers().get(HOST) + req.uri(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerInitializer.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerInitializer.java new file mode 100644 index 0000000..365e982 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/streams/websockets/WebSocketServerInitializer.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.streams.websockets; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; + +/** + * {@link WebSocketServerInitializer} is used to setup the {@link ChannelPipeline} of a {@link io.netty.channel.Channel} + * . + */ +public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> { + + @Override + protected void initChannel(final SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("codec-http", new HttpServerCodec()); + pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); + pipeline.addLast("handler", new WebSocketServerHandler()); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilder.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilder.java new file mode 100644 index 0000000..42defe5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilder.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.DatastoreIdentifier.Enumeration; + + +/** + **/ +public class DatastoreIdentifierBuilder { + + public static DatastoreIdentifier getDefaultInstance(final String defaultValue) { + return new DatastoreIdentifier(Enumeration.valueOf(defaultValue)); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilder.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilder.java new file mode 100644 index 0000000..dfd0aeb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilder.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules.Module.Revision; + +/** + * The purpose of generated class in src/main/java for Union types is to create + * new instances of unions from a string representation. In some cases it is + * very difficult to automate it since there can be unions such as (uint32 - + * uint16), or (string - uint32). + * + * The reason behind putting it under src/main/java is: This class is generated + * in form of a stub and needs to be finished by the user. This class is + * generated only once to prevent loss of user code. + * + */ +public class ModuleRevisionBuilder { + + public static Revision getDefaultInstance(java.lang.String defaultValue) { + return RevisionBuilder.getDefaultInstance(defaultValue); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilder.java b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilder.java new file mode 100644 index 0000000..d09646a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilder.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules; + +import java.util.regex.Pattern; + +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.RevisionIdentifier; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules.Module.Revision; + +/** +**/ +public class RevisionBuilder { + + /** + * Defines the pattern for revisions. NOTE: This pattern will likely be + * updated in future versions of the ietf and should be adjusted accordingly + */ + private static final Pattern REVISION_PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}"); + + public static Revision getDefaultInstance(String defaultValue) { + + if (defaultValue != null) { + if (REVISION_PATTERN.matcher(defaultValue).matches()) { + RevisionIdentifier id = new RevisionIdentifier(defaultValue); + return new Revision(id); + } + if (defaultValue.isEmpty()) { + return new Revision(defaultValue); + } + } + + throw new IllegalArgumentException("Cannot create Revision from " + defaultValue + + ". Default value does not match pattern " + REVISION_PATTERN.pattern() + + " or empty string."); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/resources/OSGI-INF/blueprint/restconf-config.xml b/netconf/restconf/restconf-nb-bierman02/src/main/resources/OSGI-INF/blueprint/restconf-config.xml new file mode 100644 index 0000000..ab235e7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/resources/OSGI-INF/blueprint/restconf-config.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2017 Inocybe Technologies Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html +--> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"> + <!-- Restconf providers --> + <cm:property-placeholder persistent-id="org.opendaylight.restconf" update-strategy="reload"> + <cm:default-properties> + <cm:property name="websocket-address" value="0.0.0.0"/> + <cm:property name="websocket-port" value="8185"/> + </cm:default-properties> + </cm:property-placeholder> + + <bean id="webSocketPort" class="org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber" factory-method="getDefaultInstance"> + <argument value="${websocket-port}"/> + </bean> + + <bean id="webSocketAddress" class="org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress"> + <argument value="${websocket-address}"/> + </bean> + + <bean id="restconfProviderDraft02" class="org.opendaylight.netconf.sal.restconf.impl.RestconfProviderImpl" + init-method="start" destroy-method="close"> + <argument ref="statisticsRestconfServiceWrapper"/> + <argument ref="webSocketAddress"/> + <argument ref="webSocketPort"/> + </bean> + + <bean id="brokerFacade" class="org.opendaylight.netconf.sal.restconf.impl.BrokerFacade" destroy-method="close"> + <argument ref="dOMRpcService"/> + <argument ref="dOMDataBroker"/> + <argument ref="dOMNotificationService"/> + <argument ref="controllerContext"/> + </bean> + <bean id="controllerContext" class="org.opendaylight.netconf.sal.restconf.impl.ControllerContext" destroy-method="close"> + <argument ref="dOMSchemaService"/> + <argument ref="dOMMountPointService"/> + <argument ref="dOMSchemaService"/> + </bean> + <bean id="jSONRestconfServiceImpl" class="org.opendaylight.netconf.sal.restconf.impl.JSONRestconfServiceImpl"> + <argument ref="controllerContext"/> + <argument ref="restconfImpl"/> + </bean> + <bean id="restconfApplication" class="org.opendaylight.netconf.sal.rest.impl.RestconfApplication"> + <argument ref="controllerContext"/> + <argument ref="statisticsRestconfServiceWrapper"/> + </bean> + <bean id="restconfImpl" class="org.opendaylight.netconf.sal.restconf.impl.RestconfImpl"> + <argument ref="brokerFacade"/> + <argument ref="controllerContext"/> + </bean> + <bean id="statisticsRestconfServiceWrapper" class="org.opendaylight.netconf.sal.restconf.impl.StatisticsRestconfServiceWrapper"> + <argument ref="restconfImpl"/> + </bean> + <bean id="webInitializer" class="org.opendaylight.netconf.sal.restconf.web.WebInitializer" destroy-method="close"> + <argument ref="webServer"/> + <argument ref="webContextSecurer"/> + <argument ref="servletSupport"/> + <argument ref="restconfApplication"/> + <argument ref="customFilterAdapterConfiguration"/> + </bean> + + <reference id="customFilterAdapterConfiguration" interface="org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration"/> + <reference id="webContextSecurer" interface="org.opendaylight.aaa.web.WebContextSecurer"/> + <reference id="webServer" interface="org.opendaylight.aaa.web.WebServer"/> + <reference id="servletSupport" interface="org.opendaylight.aaa.web.servlet.ServletSupport"/> + <reference id="dOMDataBroker" interface="org.opendaylight.mdsal.dom.api.DOMDataBroker"/> + <reference id="dOMMountPointService" interface="org.opendaylight.mdsal.dom.api.DOMMountPointService"/> + <reference id="dOMNotificationService" interface="org.opendaylight.mdsal.dom.api.DOMNotificationService"/> + <reference id="dOMRpcService" interface="org.opendaylight.mdsal.dom.api.DOMRpcService"/> + <reference id="dOMSchemaService" interface="org.opendaylight.mdsal.dom.api.DOMSchemaService"/> + <service ref="jSONRestconfServiceImpl" interface="org.opendaylight.netconf.sal.restconf.api.JSONRestconfService"/> +</blueprint> diff --git a/netconf/restconf/restconf-nb-bierman02/src/main/yang/ietf-restconf@2013-10-19.yang b/netconf/restconf/restconf-nb-bierman02/src/main/yang/ietf-restconf@2013-10-19.yang new file mode 100644 index 0000000..83bb378 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/main/yang/ietf-restconf@2013-10-19.yang @@ -0,0 +1,689 @@ +module ietf-restconf { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; revision-date 2013-07-15; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/md/sal/rest/common/TestRestconfUtils.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/md/sal/rest/common/TestRestconfUtils.java new file mode 100644 index 0000000..d883797 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/md/sal/rest/common/TestRestconfUtils.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.rest.common; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableClassToInstanceMap; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyWriter; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMMountPointService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public final class TestRestconfUtils { + + private static final Logger LOG = LoggerFactory.getLogger(TestRestconfUtils.class); + + private TestRestconfUtils() { + throw new UnsupportedOperationException("Test utility class"); + } + + public static ControllerContext newControllerContext(final EffectiveModelContext schemaContext) { + return newControllerContext(schemaContext, null); + } + + public static ControllerContext newControllerContext(final EffectiveModelContext schemaContext, + final DOMMountPoint mountInstance) { + final DOMMountPointService mockMountService = mock(DOMMountPointService.class); + + if (mountInstance != null) { + doReturn(Optional.of(FixedDOMSchemaService.of(() -> schemaContext))).when(mountInstance) + .getService(eq(DOMSchemaService.class)); + doReturn(Optional.ofNullable(mountInstance)).when(mockMountService).getMountPoint( + any(YangInstanceIdentifier.class)); + } + + DOMSchemaService mockSchemaService = mock(DOMSchemaService.class); + doReturn(schemaContext).when(mockSchemaService).getGlobalContext(); + + DOMSchemaService mockDomSchemaService = mock(DOMSchemaService.class); + doReturn(ImmutableClassToInstanceMap.of()).when(mockDomSchemaService).getExtensions(); + + return ControllerContext.newInstance(mockSchemaService, mockMountService, mockDomSchemaService); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + public static EffectiveModelContext loadSchemaContext(final String yangPath, + final EffectiveModelContext schemaContext) { + try { + Preconditions.checkArgument(yangPath != null, "Path can not be null."); + Preconditions.checkArgument(!yangPath.isEmpty(), "Path can not be empty."); + if (schemaContext == null) { + return YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles(yangPath)); + } else { + throw new UnsupportedOperationException("Unable to add new yang sources to existing schema context."); + } + } catch (final Exception e) { + LOG.error("Yang files at path: " + yangPath + " weren't loaded.", e); + } + return schemaContext; + } + + public static NormalizedNodeContext loadNormalizedContextFromJsonFile() { + throw new AbstractMethodError("Not implemented yet"); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + public static NormalizedNodeContext loadNormalizedContextFromXmlFile(final String pathToInputFile, + final String uri, final ControllerContext controllerContext) { + final InstanceIdentifierContext iiContext = controllerContext.toInstanceIdentifier(uri); + final InputStream inputStream = TestJsonBodyWriter.class.getResourceAsStream(pathToInputFile); + try { + final Document doc = UntrustedXML.newDocumentBuilder().parse(inputStream); + final NormalizedNode nn = parse(iiContext, doc); + return new NormalizedNodeContext(iiContext, nn); + } catch (final Exception e) { + LOG.error("Load xml file " + pathToInputFile + " fail.", e); + } + return null; + } + + private static NormalizedNode parse(final InstanceIdentifierContext iiContext, final Document doc) + throws XMLStreamException, IOException, SAXException, URISyntaxException { + final SchemaNode schemaNodeContext = iiContext.getSchemaNode(); + final SchemaInferenceStack stack; + DataSchemaNode schemaNode = null; + if (schemaNodeContext instanceof RpcDefinition) { + final var rpc = (RpcDefinition) schemaNodeContext; + stack = SchemaInferenceStack.of(iiContext.getSchemaContext()); + stack.enterSchemaTree(rpc.getQName()); + if ("input".equalsIgnoreCase(doc.getDocumentElement().getLocalName())) { + schemaNode = rpc.getInput(); + } else if ("output".equalsIgnoreCase(doc.getDocumentElement().getLocalName())) { + schemaNode = rpc.getOutput(); + } else { + throw new IllegalStateException("Unknown Rpc input node"); + } + stack.enterSchemaTree(schemaNode.getQName()); + } else if (schemaNodeContext instanceof DataSchemaNode) { + schemaNode = (DataSchemaNode) schemaNodeContext; + stack = iiContext.inference().toSchemaInferenceStack(); + } else { + throw new IllegalStateException("Unknow SchemaNode"); + } + + final String docRootElm = doc.getDocumentElement().getLocalName(); + final String schemaNodeName = iiContext.getSchemaNode().getQName().getLocalName(); + + if (!schemaNodeName.equalsIgnoreCase(docRootElm)) { + for (final DataSchemaNode child : ((DataNodeContainer) schemaNode).getChildNodes()) { + if (child.getQName().getLocalName().equalsIgnoreCase(docRootElm)) { + schemaNode = child; + stack.enterSchemaTree(child.getQName()); + break; + } + } + } + + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + final XmlParserStream xmlParser = XmlParserStream.create(writer, stack.toInference()); + + if (schemaNode instanceof ContainerLike || schemaNode instanceof ListSchemaNode) { + xmlParser.traverse(new DOMSource(doc.getDocumentElement())); + return resultHolder.getResult(); + } + // FIXME : add another DataSchemaNode extensions e.g. LeafSchemaNode + return null; + } + + public static Collection<File> loadFiles(final String resourceDirectory) throws FileNotFoundException { + final String path = TestRestconfUtils.class.getResource(resourceDirectory).getPath(); + final File testDir = new File(path); + final String[] fileList = testDir.list(); + final List<File> testFiles = new ArrayList<>(); + if (fileList == null) { + throw new FileNotFoundException(resourceDirectory); + } + for (final String fileName : fileList) { + if (new File(testDir, fileName).isDirectory() == false) { + testFiles.add(new File(testDir, fileName)); + } + } + return testFiles; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/InstanceIdentifierTypeLeafTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/InstanceIdentifierTypeLeafTest.java new file mode 100644 index 0000000..3c46201 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/InstanceIdentifierTypeLeafTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class InstanceIdentifierTypeLeafTest { + + @Test + public void stringToInstanceIdentifierTest() throws Exception { + final EffectiveModelContext schemaContext = + YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/instanceidentifier")); + ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + final InstanceIdentifierContext instanceIdentifier = + controllerContext.toInstanceIdentifier( + "/iid-value-module:cont-iid/iid-list/%2Fiid-value-module%3Acont-iid%2Fiid-value-module%3A" + + "values-iid%5Biid-value-module:value-iid='value'%5D"); + final YangInstanceIdentifier yiD = instanceIdentifier.getInstanceIdentifier(); + assertNotNull(yiD); + final PathArgument lastPathArgument = yiD.getLastPathArgument(); + assertTrue(lastPathArgument.getNodeType().getNamespace().toString().equals("iid:value:module")); + assertTrue(lastPathArgument.getNodeType().getLocalName().equals("iid-list")); + + final NodeIdentifierWithPredicates list = (NodeIdentifierWithPredicates) lastPathArgument; + final YangInstanceIdentifier value = (YangInstanceIdentifier) list.getValue( + QName.create(lastPathArgument.getNodeType(), "iid-leaf")); + final PathArgument lastPathArgumentOfValue = value.getLastPathArgument(); + assertTrue(lastPathArgumentOfValue.getNodeType().getNamespace().toString().equals("iid:value:module")); + assertTrue(lastPathArgumentOfValue.getNodeType().getLocalName().equals("values-iid")); + + final NodeIdentifierWithPredicates valueList = (NodeIdentifierWithPredicates) lastPathArgumentOfValue; + final String valueIid = (String) valueList.getValue( + QName.create(lastPathArgumentOfValue.getNodeType(), "value-iid")); + assertEquals("value", valueIid); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java new file mode 100644 index 0000000..5bbd161 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.net.URI; +import java.util.List; +import java.util.Optional; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Request; +import javax.ws.rs.core.UriInfo; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.netconf.sal.rest.api.RestconfConstants; +import org.opendaylight.netconf.sal.rest.impl.AbstractIdentifierAwareJaxRsProvider; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public abstract class AbstractBodyReaderTest { + private static Field uriField; + private static Field requestField; + + static { + try { + uriField = AbstractIdentifierAwareJaxRsProvider.class.getDeclaredField("uriInfo"); + uriField.setAccessible(true); + requestField = AbstractIdentifierAwareJaxRsProvider.class.getDeclaredField("request"); + requestField.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + protected final ControllerContext controllerContext; + protected final MediaType mediaType; + + protected AbstractBodyReaderTest(final EffectiveModelContext schemaContext, final DOMMountPoint mountInstance) { + mediaType = getMediaType(); + + controllerContext = TestRestconfUtils.newControllerContext(schemaContext, mountInstance); + } + + protected abstract MediaType getMediaType(); + + protected static EffectiveModelContext schemaContextLoader(final String yangPath, + final EffectiveModelContext schemaContext) { + return TestRestconfUtils.loadSchemaContext(yangPath, schemaContext); + } + + protected static <T extends AbstractIdentifierAwareJaxRsProvider> void mockBodyReader( + final String identifier, final T normalizedNodeProvider, final boolean isPost) throws Exception { + final UriInfo uriInfoMock = mock(UriInfo.class); + final MultivaluedMap<String, String> pathParm = new MultivaluedHashMap<>(1); + + if (!identifier.isEmpty()) { + pathParm.put(RestconfConstants.IDENTIFIER, List.of(identifier)); + } + + when(uriInfoMock.getPathParameters()).thenReturn(pathParm); + when(uriInfoMock.getPathParameters(false)).thenReturn(pathParm); + when(uriInfoMock.getPathParameters(true)).thenReturn(pathParm); + when(uriInfoMock.getAbsolutePath()).thenReturn(URI.create("restconf")); + uriField.set(normalizedNodeProvider, uriInfoMock); + + final Request request = mock(Request.class); + if (isPost) { + when(request.getMethod()).thenReturn("POST"); + } else { + when(request.getMethod()).thenReturn("PUT"); + } + + requestField.set(normalizedNodeProvider, request); + } + + protected static void checkMountPointNormalizedNodeContext(final NormalizedNodeContext nnContext) { + checkNormalizedNodeContext(nnContext); + assertNotNull(nnContext.getInstanceIdentifierContext().getMountPoint()); + } + + protected static void checkNormalizedNodeContext(final NormalizedNodeContext nnContext) { + assertNotNull(nnContext.getData()); + assertNotNull(nnContext.getInstanceIdentifierContext().getInstanceIdentifier()); + assertNotNull(nnContext.getInstanceIdentifierContext().getSchemaContext()); + assertNotNull(nnContext.getInstanceIdentifierContext().getSchemaNode()); + } + + protected static void checkNormalizedNodeContextRpc(final NormalizedNodeContext nnContext) { + assertNotNull(nnContext.getData()); + assertNull(nnContext.getInstanceIdentifierContext().getInstanceIdentifier()); + assertNotNull(nnContext.getInstanceIdentifierContext().getSchemaContext()); + assertNotNull(nnContext.getInstanceIdentifierContext().getSchemaNode()); + } + + protected static void checkPatchContext(final PatchContext patchContext) { + assertNotNull(patchContext.getData()); + assertNotNull(patchContext.getInstanceIdentifierContext().getInstanceIdentifier()); + assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaContext()); + assertNotNull(patchContext.getInstanceIdentifierContext().getSchemaNode()); + } + + protected static void checkPatchContextMountPoint(final PatchContext patchContext) { + checkPatchContext(patchContext); + assertNotNull(patchContext.getInstanceIdentifierContext().getMountPoint()); + } + + protected static EffectiveModelContext modelContext(final DOMMountPoint mountPoint) { + return mountPoint.getService(DOMSchemaService.class) + .flatMap(svc -> Optional.ofNullable(svc.getGlobalContext())) + .orElse(null); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReader.java new file mode 100644 index 0000000..fe50c68 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReader.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestJsonBodyReader extends AbstractBodyReaderTest { + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private static EffectiveModelContext schemaContext; + + private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create( + XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17")); + + public TestJsonBodyReader() { + super(schemaContext, null); + jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() + throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void moduleDataTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, false); + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsondata.json"); + final NormalizedNodeContext returnValue = jsonBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void moduleSubContainerDataPutTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName); + final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName); + final String uri = "instance-identifier-module:cont/cont1"; + mockBodyReader(uri, jsonBodyReader, false); + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/json_sub_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNodeOnPath, returnValue, dataII); + } + + @Test + public void moduleSubContainerDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/json_sub_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void moduleSubContainerAugmentDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next(); + final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment"); + final AugmentationIdentifier augII = new AugmentationIdentifier(Set.of(contAugmentQName)); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()) + .node(augII).node(contAugmentQName); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/json/json_augment_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + //FIXME: Uncomment this when JsonParserStream works correctly with case augmentation with choice + //@Test + public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next(); + final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1"); + final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2"); + final QName containerQName = QName.create(augmentChoice1QName, "case-choice-case-container1"); + final AugmentationIdentifier augChoice1II = new AugmentationIdentifier(Set.of(augmentChoice1QName)); + final AugmentationIdentifier augChoice2II = new AugmentationIdentifier(Set.of(augmentChoice2QName)); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()) + .node(augChoice1II).node(augmentChoice1QName).node(augChoice2II).node(augmentChoice2QName) + .node(containerQName); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/json/json_augment_choice_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "invoke-rpc-module:rpc-test"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream("/invoke-rpc/json/rpc-input.json"); + final NormalizedNodeContext returnValue = jsonBodyReader.readFrom(null, null, null, mediaType, null, + inputStream); + checkNormalizedNodeContextRpc(returnValue); + final ContainerNode inputNode = (ContainerNode) returnValue.getData(); + final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of( + QName.create(inputNode.getIdentifier().getNodeType(), "cont")); + final Optional<DataContainerChild> contDataNode = inputNode.findChildByArg(yangCont.getLastPathArgument()); + assertTrue(contDataNode.isPresent()); + assertTrue(contDataNode.get() instanceof ContainerNode); + final YangInstanceIdentifier yangleaf = YangInstanceIdentifier.of( + QName.create(inputNode.getIdentifier().getNodeType(), "lf")); + final Optional<DataContainerChild> leafDataNode = ((ContainerNode) contDataNode.get()) + .findChildByArg(yangleaf.getLastPathArgument()); + assertTrue(leafDataNode.isPresent()); + assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().body().toString())); + } + + private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode, + final NormalizedNodeContext nnContext, final YangInstanceIdentifier dataNodeIdent) { + assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode()); + assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier()); + assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReaderMountPoint.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReaderMountPoint.java new file mode 100644 index 0000000..e8776c8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyReaderMountPoint.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestJsonBodyReaderMountPoint extends AbstractBodyReaderTest { + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private static EffectiveModelContext schemaContext; + + private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create( + XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17")); + + public TestJsonBodyReaderMountPoint() throws NoSuchFieldException, SecurityException { + super(schemaContext, mock(DOMMountPoint.class)); + jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void moduleDataTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, false); + final InputStream inputStream = TestJsonBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/json/jsondata.json"); + final NormalizedNodeContext returnValue = jsonBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue); + } + + @Test + public void moduleSubContainerDataPutTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1"; + mockBodyReader(uri, jsonBodyReader, false); + final InputStream inputStream = TestJsonBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/json/json_sub_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, + QName.create(dataSchemaNode.getQName(), "cont1")); + } + + @Test + public void moduleSubContainerDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestJsonBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/json/json_sub_container.json"); + final NormalizedNodeContext returnValue = jsonBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test"; + mockBodyReader(uri, jsonBodyReader, true); + final InputStream inputStream = TestJsonBodyReaderMountPoint.class.getResourceAsStream( + "/invoke-rpc/json/rpc-input.json"); + final NormalizedNodeContext returnValue = jsonBodyReader.readFrom(null, null, null, mediaType, null, + inputStream); + checkNormalizedNodeContextRpc(returnValue); + final ContainerNode inputNode = (ContainerNode) returnValue.getData(); + final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of( + QName.create(inputNode.getIdentifier().getNodeType(), "cont")); + final Optional<DataContainerChild> contDataNode = inputNode.findChildByArg(yangCont.getLastPathArgument()); + assertTrue(contDataNode.isPresent()); + assertTrue(contDataNode.get() instanceof ContainerNode); + final YangInstanceIdentifier yangleaf = YangInstanceIdentifier.of( + QName.create(inputNode.getIdentifier().getNodeType(), "lf")); + final Optional<DataContainerChild> leafDataNode = + ((ContainerNode) contDataNode.get()).findChildByArg(yangleaf.getLastPathArgument()); + assertTrue(leafDataNode.isPresent()); + assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().body().toString())); + } + + private void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode, + final NormalizedNodeContext nnContext) { + checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null); + } + + protected void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode, + final NormalizedNodeContext nnContext, final QName qualifiedName) { + YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier.of(dataSchemaNode.getQName()); + final DOMMountPoint mountPoint = nnContext.getInstanceIdentifierContext().getMountPoint(); + final DataSchemaNode mountDataSchemaNode = modelContext(mountPoint).getDataChildByName( + dataSchemaNode.getQName()); + assertNotNull(mountDataSchemaNode); + if (qualifiedName != null && dataSchemaNode instanceof DataNodeContainer) { + final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode).getDataChildByName(qualifiedName); + dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent).node(child.getQName()).build(); + assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode().equals(child)); + } else { + assertTrue(mountDataSchemaNode.equals(dataSchemaNode)); + } + assertNotNull(NormalizedNodes.findNode(nnContext.getData(), + dataNodeIdent)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyWriter.java new file mode 100644 index 0000000..121a7d6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonBodyWriter.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestJsonBodyWriter extends AbstractBodyReaderTest { + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private final NormalizedNodeJsonBodyWriter jsonBodyWriter; + private static EffectiveModelContext schemaContext; + + public TestJsonBodyWriter() { + super(schemaContext, null); + this.jsonBodyWriter = new NormalizedNodeJsonBodyWriter(); + this.jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "invoke-rpc-module:rpc-test"; + mockBodyReader(uri, this.jsonBodyReader, true); + final InputStream inputStream = TestJsonBodyWriter.class + .getResourceAsStream("/invoke-rpc/json/rpc-output.json"); + final NormalizedNodeContext returnValue = this.jsonBodyReader.readFrom(null, + null, null, this.mediaType, null, inputStream); + final OutputStream output = new ByteArrayOutputStream(); + this.jsonBodyWriter.writeTo(returnValue, null, null, null, this.mediaType, null, + output); + assertTrue(output.toString().contains("lf-test")); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReader.java new file mode 100644 index 0000000..f2a6c41 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReader.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class TestJsonPatchBodyReader extends AbstractBodyReaderTest { + private static EffectiveModelContext schemaContext; + + private final JsonToPatchBodyReader jsonToPatchBodyReader; + + public TestJsonPatchBodyReader() { + super(schemaContext, null); + jsonToPatchBodyReader = new JsonToPatchBodyReader(controllerContext); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(APPLICATION_JSON, null); + } + + @Test + public void modulePatchDataTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdata.json"); + + final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test of successful Patch consisting of create and delete Patch operations. + */ + @Test + public void modulePatchCreateAndDeleteTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json"); + + final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test trying to use Patch create operation which requires value without value. Test should fail with + * {@link RestconfDocumentedException} with error code 400. + */ + @Test + public void modulePatchValueMissingNegativeTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdataValueMissing.json"); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test trying to use value with Patch delete operation which does not support value. Test should fail with + * {@link RestconfDocumentedException} with error code 400. + */ + @Test + public void modulePatchValueNotSupportedNegativeTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdataValueNotSupported.json"); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign. + */ + @Test + public void modulePatchCompleteTargetInURITest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json"); + + final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge. + */ + @Test + public void modulePatchMergeOperationOnListTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnList.json"); + + final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge. + */ + @Test + public void modulePatchMergeOperationOnContainerTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json"); + + final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test reading simple leaf value. + */ + @Test + public void modulePatchSimpleLeafValueTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHSimpleLeafValue.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReaderMountPoint.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReaderMountPoint.java new file mode 100644 index 0000000..4ad1a65 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReaderMountPoint.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class TestJsonPatchBodyReaderMountPoint extends AbstractBodyReaderTest { + private final JsonToPatchBodyReader jsonToPatchBodyReader; + private static EffectiveModelContext schemaContext; + private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount"; + + public TestJsonPatchBodyReaderMountPoint() { + super(schemaContext, mock(DOMMountPoint.class)); + jsonToPatchBodyReader = new JsonToPatchBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(APPLICATION_JSON, null); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext); + } + + @Test + public void modulePatchDataTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHdata.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test of successful Patch consisting of create and delete Patch operations. + */ + @Test + public void modulePatchCreateAndDeleteTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test trying to use Patch create operation which requires value without value. Test should fail with + * {@link RestconfDocumentedException} with error code 400. + */ + @Test + public void modulePatchValueMissingNegativeTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class.getResourceAsStream( + "/instanceidentifier/json/jsonPATCHdataValueMissing.json"); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test trying to use value with Patch delete operation which does not support value. Test should fail with + * {@link RestconfDocumentedException} with error code 400. + */ + @Test + public void modulePatchValueNotSupportedNegativeTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataValueNotSupported.json"); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign. + */ + @Test + public void modulePatchCompleteTargetInURITest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge. + */ + @Test + public void modulePatchMergeOperationOnListTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnList.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge. + */ + @Test + public void modulePatchMergeOperationOnContainerTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test reading simple leaf value. + */ + @Test + public void modulePatchSimpleLeafValueTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, jsonToPatchBodyReader, false); + + final InputStream inputStream = TestJsonBodyReader.class + .getResourceAsStream("/instanceidentifier/json/jsonPATCHSimpleLeafValue.json"); + + final PatchContext returnValue = jsonToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReader.java new file mode 100644 index 0000000..7ff353e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReader.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; +import javax.ws.rs.core.MediaType; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestXmlBodyReader extends AbstractBodyReaderTest { + + private final XmlNormalizedNodeBodyReader xmlBodyReader; + private static EffectiveModelContext schemaContext; + private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create( + XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17")); + + public TestXmlBodyReader() { + super(schemaContext, null); + xmlBodyReader = new XmlNormalizedNodeBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + testFiles.addAll(TestRestconfUtils.loadFiles("/foo-xml-test/yang")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void putXmlTest() throws Exception { + runXmlTest(false, "foo:top-level-list/key-value"); + } + + @Test + public void postXmlTest() throws Exception { + runXmlTest(true, ""); + } + + private void runXmlTest(final boolean isPost, final String path) throws Exception { + mockBodyReader(path, xmlBodyReader, isPost); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream("/foo-xml-test/foo.xml"); + final NormalizedNodeContext nnc = xmlBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + assertNotNull(nnc); + + assertTrue(nnc.getData() instanceof MapEntryNode); + final MapEntryNode data = (MapEntryNode) nnc.getData(); + assertEquals(2, data.size()); + for (final DataContainerChild child : data.body()) { + switch (child.getIdentifier().getNodeType().getLocalName()) { + case "key-leaf": + assertEquals("key-value", child.body()); + break; + + case "ordinary-leaf": + assertEquals("leaf-value", child.body()); + break; + default: + fail(); + } + } + } + + @Test + public void moduleDataTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmldata.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void moduleSubContainerDataPutTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName); + final DataSchemaNode dataSchemaNodeOnPath = ((DataNodeContainer) dataSchemaNode).getDataChildByName(cont1QName); + final String uri = "instance-identifier-module:cont/cont1"; + mockBodyReader(uri, xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNodeOnPath, returnValue, dataII); + } + + @Test + public void moduleSubContainerDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final QName cont1QName = QName.create(dataSchemaNode.getQName(), "cont1"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()).node(cont1QName); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void moduleSubContainerAugmentDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next(); + final QName contAugmentQName = QName.create(augmentModule.getQNameModule(), "cont-augment"); + final AugmentationIdentifier augII = new AugmentationIdentifier(Set.of(contAugmentQName)); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()) + .node(augII).node(contAugmentQName); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xml_augment_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void moduleSubContainerChoiceAugmentDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = + schemaContext.getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final Module augmentModule = schemaContext.findModules(XMLNamespace.of("augment:module")).iterator().next(); + final QName augmentChoice1QName = QName.create(augmentModule.getQNameModule(), "augment-choice1"); + final QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2"); + final YangInstanceIdentifier dataII = YangInstanceIdentifier.of(dataSchemaNode.getQName()) + .node(new AugmentationIdentifier(Set.of(augmentChoice1QName))) + .node(augmentChoice1QName) + // FIXME: DataSchemaTreeNode intepretation seems to have a bug + //.node(new AugmentationIdentifier(Set.of(augmentChoice2QName))) + .node(augmentChoice2QName) + .node(QName.create(augmentChoice1QName, "case-choice-case-container1")); + final String uri = "instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xml_augment_choice_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, dataII); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "invoke-rpc-module:rpc-test"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream("/invoke-rpc/xml/rpc-input.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader.readFrom(null, null, null, mediaType, null, + inputStream); + checkNormalizedNodeContextRpc(returnValue); + final ContainerNode contNode = (ContainerNode) returnValue.getData(); + final Optional<DataContainerChild> contDataNodePotential = contNode.findChildByArg(new NodeIdentifier( + QName.create(contNode.getIdentifier().getNodeType(), "cont"))); + assertTrue(contDataNodePotential.isPresent()); + final ContainerNode contDataNode = (ContainerNode) contDataNodePotential.get(); + final Optional<DataContainerChild> leafDataNode = contDataNode.findChildByArg(new NodeIdentifier( + QName.create(contDataNode.getIdentifier().getNodeType(), "lf"))); + assertTrue(leafDataNode.isPresent()); + assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().body().toString())); + } + + private static void checkExpectValueNormalizeNodeContext(final DataSchemaNode dataSchemaNode, + final NormalizedNodeContext nnContext, final YangInstanceIdentifier dataNodeIdent) { + assertEquals(dataSchemaNode, nnContext.getInstanceIdentifierContext().getSchemaNode()); + assertEquals(dataNodeIdent, nnContext.getInstanceIdentifierContext().getInstanceIdentifier()); + assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent)); + } + + /** + * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be + * used to distinguish between them to find correct one. Check if container was found not only according to its name + * but also by correct namespace used in payload. + */ + @Test + public void findFooContainerUsingNamespaceTest() throws Exception { + mockBodyReader("", xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + + // check return value + checkNormalizedNodeContext(returnValue); + // check if container was found both according to its name and namespace + assertEquals("Not correct container found, name was ignored", + "foo-bar-container", returnValue.getData().getIdentifier().getNodeType().getLocalName()); + assertEquals("Not correct container found, namespace was ignored", + "foo:module", returnValue.getData().getIdentifier().getNodeType().getNamespace().toString()); + } + + /** + * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be + * used to distinguish between them to find correct one. Check if container was found not only according to its name + * but also by correct namespace used in payload. + */ + @Test + public void findBarContainerUsingNamespaceTest() throws Exception { + mockBodyReader("", xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + + // check return value + checkNormalizedNodeContext(returnValue); + // check if container was found both according to its name and namespace + assertEquals("Not correct container found, name was ignored", + "foo-bar-container", returnValue.getData().getIdentifier().getNodeType().getLocalName()); + assertEquals("Not correct container found, namespace was ignored", + "bar:module", returnValue.getData().getIdentifier().getNodeType().getNamespace().toString()); + } + + /** + * Test PUT operation when message root element is not the same as the last element in request URI. + * PUT operation message should always start with schema node from URI otherwise exception should be + * thrown. + */ + @Test + public void wrongRootElementTest() throws Exception { + mockBodyReader("instance-identifier-module:cont", xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/bug7933.xml"); + try { + xmlBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + Assert.fail("Test should fail due to malformed PUT operation message"); + } catch (final RestconfDocumentedException exception) { + final RestconfError restconfError = exception.getErrors().get(0); + assertEquals(ErrorType.PROTOCOL, restconfError.getErrorType()); + assertEquals(ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag()); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReaderMountPoint.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReaderMountPoint.java new file mode 100644 index 0000000..fe257db --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyReaderMountPoint.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.InputStream; +import java.util.Collection; +import java.util.Optional; +import javax.ws.rs.core.MediaType; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestXmlBodyReaderMountPoint extends AbstractBodyReaderTest { + private final XmlNormalizedNodeBodyReader xmlBodyReader; + private static EffectiveModelContext schemaContext; + + private static final QNameModule INSTANCE_IDENTIFIER_MODULE_QNAME = QNameModule.create( + XMLNamespace.of("instance:identifier:module"), Revision.of("2014-01-17")); + + public TestXmlBodyReaderMountPoint() { + super(schemaContext, mock(DOMMountPoint.class)); + xmlBodyReader = new XmlNormalizedNodeBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void moduleDataTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/xml/xmldata.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue); + } + + @Test + public void moduleSubContainerDataPutTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont/cont1"; + mockBodyReader(uri, xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue, + QName.create(dataSchemaNode.getQName(), "cont1")); + } + + @Test + public void moduleSubContainerDataPostTest() throws Exception { + final DataSchemaNode dataSchemaNode = schemaContext + .getDataChildByName(QName.create(INSTANCE_IDENTIFIER_MODULE_QNAME, "cont")); + final String uri = "instance-identifier-module:cont/yang-ext:mount/instance-identifier-module:cont"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReaderMountPoint.class + .getResourceAsStream("/instanceidentifier/xml/xml_sub_container.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkMountPointNormalizedNodeContext(returnValue); + checkExpectValueNormalizeNodeContext(dataSchemaNode, returnValue); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "instance-identifier-module:cont/yang-ext:mount/invoke-rpc-module:rpc-test"; + mockBodyReader(uri, xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReaderMountPoint.class + .getResourceAsStream("/invoke-rpc/xml/rpc-input.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader.readFrom(null, + null, null, mediaType, null, inputStream); + checkNormalizedNodeContextRpc(returnValue); + final ContainerNode contNode = (ContainerNode) returnValue.getData(); + final YangInstanceIdentifier yangCont = YangInstanceIdentifier.of( + QName.create(contNode.getIdentifier().getNodeType(), "cont")); + final Optional<DataContainerChild> contDataNodePotential = + contNode.findChildByArg(yangCont.getLastPathArgument()); + assertTrue(contDataNodePotential.isPresent()); + final ContainerNode contDataNode = (ContainerNode) contDataNodePotential.get(); + final YangInstanceIdentifier yangLeaf = YangInstanceIdentifier.of( + QName.create(contDataNode.getIdentifier().getNodeType(), "lf")); + final Optional<DataContainerChild> leafDataNode = contDataNode.findChildByArg(yangLeaf.getLastPathArgument()); + assertTrue(leafDataNode.isPresent()); + assertTrue("lf-test".equalsIgnoreCase(leafDataNode.get().body().toString())); + } + + private void checkExpectValueNormalizeNodeContext( + final DataSchemaNode dataSchemaNode, + final NormalizedNodeContext nnContext) { + checkExpectValueNormalizeNodeContext(dataSchemaNode, nnContext, null); + } + + protected void checkExpectValueNormalizeNodeContext( + final DataSchemaNode dataSchemaNode, final NormalizedNodeContext nnContext, final QName qualifiedName) { + YangInstanceIdentifier dataNodeIdent = YangInstanceIdentifier.of(dataSchemaNode.getQName()); + final DOMMountPoint mountPoint = nnContext.getInstanceIdentifierContext().getMountPoint(); + final DataSchemaNode mountDataSchemaNode = + modelContext(mountPoint).getDataChildByName(dataSchemaNode.getQName()); + assertNotNull(mountDataSchemaNode); + if (qualifiedName != null && dataSchemaNode instanceof DataNodeContainer) { + final DataSchemaNode child = ((DataNodeContainer) dataSchemaNode).getDataChildByName(qualifiedName); + dataNodeIdent = YangInstanceIdentifier.builder(dataNodeIdent).node(child.getQName()).build(); + assertTrue(nnContext.getInstanceIdentifierContext().getSchemaNode().equals(child)); + } else { + assertTrue(mountDataSchemaNode.equals(dataSchemaNode)); + } + assertNotNull(NormalizedNodes.findNode(nnContext.getData(), dataNodeIdent)); + } + + /** + * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be + * used to distinguish between them to find correct one. Check if container was found not only according to its name + * but also by correct namespace used in payload. + */ + @Test + public void findFooContainerUsingNamespaceTest() throws Exception { + mockBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlDataFindFooContainer.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + + // check return value + checkMountPointNormalizedNodeContext(returnValue); + // check if container was found both according to its name and namespace + assertEquals("Not correct container found, name was ignored", + "foo-bar-container", returnValue.getData().getIdentifier().getNodeType().getLocalName()); + assertEquals("Not correct container found, namespace was ignored", + "foo:module", returnValue.getData().getIdentifier().getNodeType().getNamespace().toString()); + } + + /** + * Test when container with the same name is placed in two modules (foo-module and bar-module). Namespace must be + * used to distinguish between them to find correct one. Check if container was found not only according to its name + * but also by correct namespace used in payload. + */ + @Test + public void findBarContainerUsingNamespaceTest() throws Exception { + mockBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader, true); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlDataFindBarContainer.xml"); + final NormalizedNodeContext returnValue = xmlBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + + // check return value + checkMountPointNormalizedNodeContext(returnValue); + // check if container was found both according to its name and namespace + assertEquals("Not correct container found, name was ignored", + "foo-bar-container", returnValue.getData().getIdentifier().getNodeType().getLocalName()); + assertEquals("Not correct container found, namespace was ignored", + "bar:module", returnValue.getData().getIdentifier().getNodeType().getNamespace().toString()); + } + + /** + * Test PUT operation when message root element is not the same as the last element in request URI. + * PUT operation message should always start with schema node from URI otherwise exception should be + * thrown. + */ + @Test + public void wrongRootElementTest() throws Exception { + mockBodyReader("instance-identifier-module:cont/yang-ext:mount", xmlBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/bug7933.xml"); + try { + xmlBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + Assert.fail("Test should fail due to malformed PUT operation message"); + } catch (final RestconfDocumentedException exception) { + final RestconfError restconfError = exception.getErrors().get(0); + assertEquals(ErrorType.PROTOCOL, restconfError.getErrorType()); + assertEquals(ErrorTag.MALFORMED_MESSAGE, restconfError.getErrorTag()); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyWriter.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyWriter.java new file mode 100644 index 0000000..169d806 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlBodyWriter.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.OutputStream; +import java.util.Collection; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class TestXmlBodyWriter extends AbstractBodyReaderTest { + + private final NormalizedNodeXmlBodyWriter xmlBodyWriter; + private static EffectiveModelContext schemaContext; + + public TestXmlBodyWriter() { + super(schemaContext, null); + this.xmlBodyWriter = new NormalizedNodeXmlBodyWriter(); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws Exception { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/instanceidentifier/yang"); + testFiles.addAll(TestRestconfUtils.loadFiles("/invoke-rpc")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void rpcModuleInputTest() throws Exception { + final String uri = "invoke-rpc-module:rpc-test"; + final String pathToInputFile = "/invoke-rpc/xml/rpc-output.xml"; + final NormalizedNodeContext nnContext = TestRestconfUtils + .loadNormalizedContextFromXmlFile(pathToInputFile, uri, controllerContext); + final OutputStream output = new ByteArrayOutputStream(); + this.xmlBodyWriter.writeTo(nnContext, null, null, null, this.mediaType, null, output); + assertTrue(output.toString().contains("lf-test")); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReader.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReader.java new file mode 100644 index 0000000..8b37ba4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReader.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.netconf.sal.rest.impl.XmlToPatchBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class TestXmlPatchBodyReader extends AbstractBodyReaderTest { + + private final XmlToPatchBodyReader xmlToPatchBodyReader; + private static EffectiveModelContext schemaContext; + + public TestXmlPatchBodyReader() { + super(schemaContext, null); + xmlToPatchBodyReader = new XmlToPatchBodyReader(controllerContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @BeforeClass + public static void initialization() throws NoSuchFieldException, SecurityException { + schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext); + } + + @Test + public void moduleDataTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdata.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned. + */ + @Test + public void moduleDataValueMissingNegativeTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataValueMissing.xml"); + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(1, ex.getErrors().size()); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be + * returned. + */ + @Test + public void moduleDataNotValueNotSupportedNegativeTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml"); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(1, ex.getErrors().size()); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test of Yang Patch with absolute target path. + */ + @Test + public void moduleDataAbsoluteTargetPathTest() throws Exception { + final String uri = ""; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign. + */ + @Test + public void modulePatchCompleteTargetInURITest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml"); + final PatchContext returnValue = xmlToPatchBodyReader + .readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge. + */ + @Test + public void moduleDataMergeOperationOnListTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } + + /** + * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge. + */ + @Test + public void moduleDataMergeOperationOnContainerTest() throws Exception { + final String uri = "instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContext(returnValue); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReaderMountPoint.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReaderMountPoint.java new file mode 100644 index 0000000..614f36e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReaderMountPoint.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.impl.test.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.netconf.sal.rest.impl.XmlToPatchBodyReader; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class TestXmlPatchBodyReaderMountPoint extends AbstractBodyReaderTest { + + private final XmlToPatchBodyReader xmlToPatchBodyReader; + private static EffectiveModelContext schemaContext; + private static final String MOUNT_POINT = "instance-identifier-module:cont/yang-ext:mount"; + + public TestXmlPatchBodyReaderMountPoint() { + super(schemaContext, mock(DOMMountPoint.class)); + xmlToPatchBodyReader = new XmlToPatchBodyReader(controllerContext); + } + + @BeforeClass + public static void initialization() throws NoSuchFieldException, SecurityException { + schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } + + @Test + public void moduleDataTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdata.xml"); + checkPatchContextMountPoint(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + } + + /** + * Test trying to use Patch create operation which requires value without value. Error code 400 should be returned. + */ + @Test + public void moduleDataValueMissingNegativeTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataValueMissing.xml"); + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test trying to use value with Patch delete operation which does not support value. Error code 400 should be + * returned. + */ + @Test + public void moduleDataNotValueNotSupportedNegativeTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class + .getResourceAsStream("/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml"); + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + assertEquals(ErrorTag.MALFORMED_MESSAGE, ex.getErrors().get(0).getErrorTag()); + } + + /** + * Test of Yang Patch with absolute target path. + */ + @Test + public void moduleDataAbsoluteTargetPathTest() throws Exception { + final String uri = MOUNT_POINT; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test using Patch when target is completely specified in request URI and thus target leaf contains only '/' sign. + */ + @Test + public void modulePatchCompleteTargetInURITest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml"); + final PatchContext returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream); + checkPatchContextMountPoint(returnValue); + } + + /** + * Test of Yang Patch merge operation on list. Test consists of two edit operations - replace and merge. + */ + @Test + public void moduleDataMergeOperationOnListTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml"); + checkPatchContextMountPoint(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + } + + /** + * Test of Yang Patch merge operation on container. Test consists of two edit operations - create and merge. + */ + @Test + public void moduleDataMergeOperationOnContainerTest() throws Exception { + final String uri = MOUNT_POINT + "/instance-identifier-patch-module:patch-cont"; + mockBodyReader(uri, xmlToPatchBodyReader, false); + final InputStream inputStream = TestXmlBodyReader.class.getResourceAsStream( + "/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml"); + checkPatchContextMountPoint(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java new file mode 100644 index 0000000..e263873 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonBasicDataTypesTest.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.cnsn.to.json.test; + +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.fail; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import org.junit.BeforeClass; +import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; + +public class CnSnToJsonBasicDataTypesTest extends YangAndXmlAndDataSchemaLoader { + + abstract static class LeafVerifier { + + Object expectedValue; + JsonToken expectedToken; + + LeafVerifier(final Object expectedValue, final JsonToken expectedToken) { + this.expectedValue = expectedValue; + this.expectedToken = expectedToken; + } + + abstract Object getActualValue(JsonReader reader) throws IOException; + + void verify(final JsonReader reader, final String keyName) throws IOException { + assertEquals("Json value for key " + keyName, this.expectedValue, getActualValue(reader)); + } + + JsonToken expectedTokenType() { + return this.expectedToken; + } + } + + static class BooleanVerifier extends LeafVerifier { + + BooleanVerifier(final boolean expected) { + super(expected, JsonToken.BOOLEAN); + } + + @Override + Object getActualValue(final JsonReader reader) throws IOException { + return reader.nextBoolean(); + } + } + + static class NumberVerifier extends LeafVerifier { + + NumberVerifier(final Number expected) { + super(expected, JsonToken.NUMBER); + } + + @Override + Object getActualValue(final JsonReader reader) throws IOException { + if (this.expectedValue instanceof Double) { + return reader.nextDouble(); + } else if (this.expectedValue instanceof Long) { + return reader.nextLong(); + } else if (this.expectedValue instanceof Integer) { + return reader.nextInt(); + } + + return null; + } + } + + static class StringVerifier extends LeafVerifier { + + StringVerifier(final String expected) { + super(expected, JsonToken.STRING); + } + + @Override + Object getActualValue(final JsonReader reader) throws IOException { + return reader.nextString(); + } + } + + static class EmptyVerifier extends LeafVerifier { + + EmptyVerifier() { + super(null, null); + } + + @Override + Object getActualValue(final JsonReader reader) throws IOException { + reader.beginArray(); + reader.nextNull(); + reader.endArray(); + return null; + } + + } + + static class ComplexAnyXmlVerifier extends LeafVerifier { + + ComplexAnyXmlVerifier() { + super(null, JsonToken.BEGIN_OBJECT); + } + + @Override + void verify(final JsonReader reader, final String keyName) throws IOException { + + reader.beginObject(); + final String innerKey = reader.nextName(); + assertEquals("Json reader child key for " + keyName, "data", innerKey); + assertEquals("Json token type for key " + innerKey, JsonToken.BEGIN_OBJECT, reader.peek()); + + reader.beginObject(); + verifyLeaf(reader, innerKey, "leaf1", "leaf1-value"); + verifyLeaf(reader, innerKey, "leaf2", "leaf2-value"); + + String nextName = reader.nextName(); + assertEquals("Json reader child key for " + innerKey, "leaf-list", nextName); + reader.beginArray(); + assertEquals("Json value for key " + nextName, "leaf-list-value1", reader.nextString()); + assertEquals("Json value for key " + nextName, "leaf-list-value2", reader.nextString()); + reader.endArray(); + + nextName = reader.nextName(); + assertEquals("Json reader child key for " + innerKey, "list", nextName); + reader.beginArray(); + verifyNestedLists(reader, 1); + verifyNestedLists(reader, 3); + reader.endArray(); + + reader.endObject(); + reader.endObject(); + } + + void verifyNestedLists(final JsonReader reader, int leafNum) throws IOException { + reader.beginObject(); + + final String nextName = reader.nextName(); + assertEquals("Json reader next name", "nested-list", nextName); + + reader.beginArray(); + + reader.beginObject(); + verifyLeaf(reader, "nested-list", "nested-leaf", "nested-value" + leafNum++); + reader.endObject(); + + reader.beginObject(); + verifyLeaf(reader, "nested-list", "nested-leaf", "nested-value" + leafNum); + reader.endObject(); + + reader.endArray(); + reader.endObject(); + } + + void verifyLeaf(final JsonReader reader, final String parent, final String name, + final String value) throws IOException { + final String nextName = reader.nextName(); + assertEquals("Json reader child key for " + parent, name, nextName); + assertEquals("Json token type for key " + parent, JsonToken.STRING, reader.peek()); + assertEquals("Json value for key " + nextName, value, reader.nextString()); + } + + @Override + Object getActualValue(final JsonReader reader) throws IOException { + return null; + } + } + + @BeforeClass + public static void initialize() throws FileNotFoundException { + dataLoad("/cnsn-to-json/simple-data-types"); + } + + private static void verifyJsonOutput(final String jsonOutput) { + final StringReader strReader = new StringReader(jsonOutput); + final JsonReader jReader = new JsonReader(strReader); + + String exception = null; + try { + jsonReadCont(jReader); + } catch (final IOException e) { + exception = e.getMessage(); + } + + assertNull("Error during reading Json output: " + exception, exception); + } + + private static void jsonReadCont(final JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + assertNotNull("cont1 is missing.", jsonReader.hasNext()); + + // Cont dataFromJson = new Cont(jReader.nextName()); + jsonReader.nextName(); + jsonReadContElements(jsonReader); + + assertFalse("cont shouldn't have other element.", jsonReader.hasNext()); + jsonReader.endObject(); + // return dataFromJson; + } + + private static void jsonReadContElements(final JsonReader jsonReader) throws IOException { + jsonReader.beginObject(); + + final Map<String, LeafVerifier> expectedMap = new HashMap<>(); + expectedMap.put("lfnint8Min", new NumberVerifier(-128)); + expectedMap.put("lfnint8Max", new NumberVerifier(127)); + expectedMap.put("lfnint16Min", new NumberVerifier(-32768)); + expectedMap.put("lfnint16Max", new NumberVerifier(32767)); + expectedMap.put("lfnint32Min", new NumberVerifier(-2147483648)); + expectedMap.put("lfnint32Max", new NumberVerifier(2147483647L)); + expectedMap.put("lfnint64Min", new NumberVerifier(-9223372036854775808L)); + expectedMap.put("lfnint64Max", new NumberVerifier(9223372036854775807L)); + expectedMap.put("lfnuint8Max", new NumberVerifier(255)); + expectedMap.put("lfnuint16Max", new NumberVerifier(65535)); + expectedMap.put("lfnuint32Max", new NumberVerifier(4294967295L)); + expectedMap.put("lfstr", new StringVerifier("lfstr")); + expectedMap.put("lfstr1", new StringVerifier("")); + expectedMap.put("lfbool1", new BooleanVerifier(true)); + expectedMap.put("lfbool2", new BooleanVerifier(false)); + expectedMap.put("lfbool3", new BooleanVerifier(false)); + expectedMap.put("lfdecimal1", new NumberVerifier(43.32)); + expectedMap.put("lfdecimal2", new NumberVerifier(-0.43)); + expectedMap.put("lfdecimal3", new NumberVerifier(43d)); + expectedMap.put("lfdecimal4", new NumberVerifier(43E3)); + expectedMap.put("lfdecimal6", new NumberVerifier(33.12345)); + expectedMap.put("lfenum", new StringVerifier("enum3")); + expectedMap.put("lfbits", new StringVerifier("bit3 bit2")); + expectedMap.put("lfbinary", new StringVerifier("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); + expectedMap.put("lfunion1", new StringVerifier("324")); + expectedMap.put("lfunion2", new StringVerifier("33.3")); + expectedMap.put("lfunion3", new StringVerifier("55")); + expectedMap.put("lfunion4", new StringVerifier("true")); + expectedMap.put("lfunion5", new StringVerifier("true")); + expectedMap.put("lfunion6", new StringVerifier("10")); + expectedMap.put("lfunion7", new StringVerifier("")); + expectedMap.put("lfunion8", new StringVerifier("")); + expectedMap.put("lfunion9", new StringVerifier("")); + expectedMap.put("lfunion10", new StringVerifier("bt1")); + expectedMap.put("lfunion11", new StringVerifier("33")); + expectedMap.put("lfunion12", new StringVerifier("false")); + expectedMap.put("lfunion13", new StringVerifier("b1")); + expectedMap.put("lfunion14", new StringVerifier("zero")); + expectedMap.put("lfempty", new EmptyVerifier()); + expectedMap.put("identityref1", new StringVerifier("simple-data-types:iden")); + expectedMap.put("complex-any", new ComplexAnyXmlVerifier()); + expectedMap.put("simple-any", new StringVerifier("simple")); + expectedMap.put("empty-any", new StringVerifier("")); + + while (jsonReader.hasNext()) { + final String keyName = jsonReader.nextName(); + final JsonToken peek = jsonReader.peek(); + + final LeafVerifier verifier = expectedMap.remove(keyName); + assertNotNull("Found unexpected leaf: " + keyName, verifier); + + final JsonToken expToken = verifier.expectedTokenType(); + if (expToken != null) { + assertEquals("Json token type for key " + keyName, expToken, peek); + } + + verifier.verify(jsonReader, keyName); + } + + if (!expectedMap.isEmpty()) { + fail("Missing leaf nodes in Json output: " + expectedMap.keySet()); + } + + jsonReader.endObject(); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonIdentityrefTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonIdentityrefTest.java new file mode 100644 index 0000000..bf262d7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonIdentityrefTest.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.cnsn.to.json.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class CnSnToJsonIdentityrefTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialization() throws FileNotFoundException, ReactorException { + dataLoad("/cnsn-to-json/identityref", 2, "identityref-module", "cont"); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonWithDataFromSeveralModulesTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonWithDataFromSeveralModulesTest.java new file mode 100644 index 0000000..1b44d75 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonWithDataFromSeveralModulesTest.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.cnsn.to.json.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class CnSnToJsonWithDataFromSeveralModulesTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + dataLoad("/xml-to-cnsn/data-of-several-modules/yang", 2, "module1", "cont_m1"); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/input/to/cnsn/test/RestPutListDataTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/input/to/cnsn/test/RestPutListDataTest.java new file mode 100644 index 0000000..71406ca --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/input/to/cnsn/test/RestPutListDataTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.input.to.cnsn.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.util.concurrent.FluentFuture; +import java.io.FileNotFoundException; +import java.util.List; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.PutResult; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid.DataValidationException; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +public class RestPutListDataTest { + private static EffectiveModelContext schemaContextTestModule; + + private static BrokerFacade brokerFacade; + private static RestconfImpl restconfImpl; + + private static final String TEST_MODULE_NS_STRING = "test:module"; + private static final String TEST_MODULE_REVISION = "2014-01-09"; + + @BeforeClass + public static void staticSetup() throws FileNotFoundException { + schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module"); + } + + @Before + public void initialize() throws FileNotFoundException { + final ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContextTestModule); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + final PutResult result = mock(PutResult.class); + when(brokerFacade.commitConfigurationDataPut(any(EffectiveModelContext.class), + any(YangInstanceIdentifier.class), any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString())) + .thenReturn(result); + when(result.getFutureOfPutData()).thenReturn(mock(FluentFuture.class)); + when(result.getStatus()).thenReturn(Status.OK); + } + + /** + * Tests whether no exception is raised if number and values of keys in URI + * and payload are equal. + */ + @Test + @Ignore + public void testValidKeys() { + putListDataTest("key1value", "15", "key1value", (short) 15); + } + + /** + * Tests whether an exception is raised if key values in URI and payload are + * different. + * + * <p> + * The exception should be raised from validation method + * {@code RestconfImpl#validateListEqualityOfListInDataAndUri} + */ + @Test + @Ignore // RestconfDocumentedExceptionMapper needs update + public void testUriAndPayloadKeysDifferent() { + try { + putListDataTest("key1value", "15", "key1value", (short) 16); + fail("RestconfDocumentedException expected"); + } catch (final RestconfDocumentedException e) { + verifyException(e, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + + try { + putListDataTest("key1value", "15", "key1value1", (short) 16); + fail("RestconfDocumentedException expected"); + } catch (final RestconfDocumentedException e) { + verifyException(e, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + } + } + + /** + * Tests whether an exception is raised if URI contains less key values then + * payload. + * + * <p> + * The exception is raised during {@code InstanceIdentifier} instance is + * built from URI + */ + @Test + @Ignore + public void testMissingKeysInUri() { + try { + putListDataTest("key1value", null, "key1value", (short) 15); + fail("RestconfDocumentedException expected"); + } catch (final RestconfDocumentedException e) { + verifyException(e, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); + } + } + + /** + * Tests whether an exception is raised if URI contains more key values then + * payload. + * + * <p> + * The exception should be raised from validation method + * {@code RestconfImpl#validateListEqualityOfListInDataAndUri} + */ + @Test + public void testMissingKeysInPayload() { + try { + putListDataTest("key1value", "15", "key1value", null); + fail("RestconfDocumentedException expected"); + } catch (final DataValidationException e) { + // FIXME: thing about different approach for testing the Exception states + // RestconfDocumentedException is not rise in new API because you get + // DataValidationException from putListDataTest before you call the real rest service +// verifyException(e, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); + } + } + + private static void verifyException(final RestconfDocumentedException restDocumentedException, + final ErrorType errorType, final ErrorTag errorTag) { + final List<RestconfError> errors = restDocumentedException.getErrors(); + assertEquals("getErrors() size", 1, errors.size()); + assertEquals("RestconfError getErrorType()", errorType, errors.get(0).getErrorType()); + assertEquals("RestconfError getErrorTag()", errorTag, errors.get(0).getErrorTag()); + } + + public void putListDataTest(final String uriKey1, final String uriKey2, final String payloadKey1, + final Short payloadKey2) { + final QName lstWithCompositeKey = + QName.create(TEST_MODULE_NS_STRING, TEST_MODULE_REVISION, "lst-with-composite-key"); + final QName key1 = QName.create(TEST_MODULE_NS_STRING, TEST_MODULE_REVISION, "key1"); + final QName key2 = QName.create(TEST_MODULE_NS_STRING, TEST_MODULE_REVISION, "key2"); + + final DataSchemaNode testNodeSchemaNode = schemaContextTestModule.getDataChildByName(lstWithCompositeKey); + assertTrue(testNodeSchemaNode != null); + assertTrue(testNodeSchemaNode instanceof ListSchemaNode); + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> testNodeContainer = + SchemaAwareBuilders.mapEntryBuilder((ListSchemaNode) testNodeSchemaNode); + + var testChildren = ControllerContext.findInstanceDataChildrenByName( + (ListSchemaNode) testNodeSchemaNode, key1.getLocalName()); + assertTrue(testChildren != null); + final DataSchemaNode testLeafKey1SchemaNode = testChildren.get(0).child; + assertTrue(testLeafKey1SchemaNode != null); + assertTrue(testLeafKey1SchemaNode instanceof LeafSchemaNode); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafKey1 = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) testLeafKey1SchemaNode); + leafKey1.withValue(payloadKey1); + testNodeContainer.withChild(leafKey1.build()); + + if (payloadKey2 != null) { + testChildren = ControllerContext.findInstanceDataChildrenByName( + (ListSchemaNode) testNodeSchemaNode, key2.getLocalName()); + assertTrue(testChildren != null); + final DataSchemaNode testLeafKey2SchemaNode = testChildren.get(0).child; + assertNotNull(testLeafKey2SchemaNode); + assertTrue(testLeafKey2SchemaNode instanceof LeafSchemaNode); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafKey2 = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) testLeafKey2SchemaNode); + leafKey2.withValue(payloadKey2); + testNodeContainer.withChild(leafKey2.build()); + } + + final NormalizedNodeContext testCompositeContext = new NormalizedNodeContext( + InstanceIdentifierContext.ofStack( + SchemaInferenceStack.ofDataTreePath(schemaContextTestModule, lstWithCompositeKey)), + testNodeContainer.build()); + + final UriInfo uriInfo = Mockito.mock(UriInfo.class); + restconfImpl.updateConfigurationData(toUri(uriKey1, uriKey2), testCompositeContext, uriInfo); + } + + public void putListDataWithWrapperTest(final String uriKey1, final String uriKey2, final String payloadKey1, + final Short payloadKey2) { + putListDataTest(uriKey1, uriKey2, payloadKey1, payloadKey2); + } + + private static String toUri(final String uriKey1, final String uriKey2) { + final StringBuilder uriBuilder = new StringBuilder("/test-module:lst-with-composite-key/"); + uriBuilder.append(uriKey1); + if (uriKey2 != null) { + uriBuilder.append("/"); + uriBuilder.append(uriKey2); + } + return uriBuilder.toString(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonIdentityrefToNnTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonIdentityrefToNnTest.java new file mode 100644 index 0000000..f331c47 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonIdentityrefToNnTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.json.to.nn.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class JsonIdentityrefToNnTest extends AbstractBodyReaderTest { + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private static EffectiveModelContext schemaContext; + + public JsonIdentityrefToNnTest() { + super(schemaContext, null); + this.jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @BeforeClass + public static void initialize() { + schemaContext = schemaContextLoader("/json-to-nn/identityref", schemaContext); + } + + @Test + public void jsonIdentityrefToNn() throws Exception { + + final String uri = "identityref-module:cont"; + mockBodyReader(uri, this.jsonBodyReader, false); + final InputStream inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/identityref/json/data.json"); + + final NormalizedNodeContext normalizedNodeContext = this.jsonBodyReader.readFrom( + null, null, null, this.mediaType, null, inputStream); + + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + + assertTrue(dataTree.contains("cont1")); + assertTrue(dataTree + .contains("lf11 (identity:module?revision=2013-12-02)iden")); + assertTrue(dataTree + .contains("lf12 (identityref:module?revision=2013-12-02)iden_local")); + assertTrue(dataTree + .contains("lf13 (identityref:module?revision=2013-12-02)iden_local")); + assertTrue(dataTree + .contains("lf14 (identity:module?revision=2013-12-02)iden")); + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonLeafrefToNnTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonLeafrefToNnTest.java new file mode 100644 index 0000000..19bba7b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonLeafrefToNnTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.json.to.nn.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class JsonLeafrefToNnTest extends AbstractBodyReaderTest { + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private static EffectiveModelContext schemaContext; + + public JsonLeafrefToNnTest() { + super(schemaContext, null); + this.jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @BeforeClass + public static void initialize() { + schemaContext = schemaContextLoader("/json-to-nn/leafref", schemaContext); + } + + @Test + public void jsonIdentityrefToNormalizeNode() throws Exception { + + final String uri = "leafref-module:cont"; + mockBodyReader(uri, this.jsonBodyReader, false); + final InputStream inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/leafref/json/data.json"); + + final NormalizedNodeContext normalizedNodeContext = this.jsonBodyReader.readFrom( + null, null, null, this.mediaType, null, inputStream); + + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("lf2 121")); + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonToNnTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonToNnTest.java new file mode 100644 index 0000000..51d1e64 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/json/to/nn/test/JsonToNnTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.json.to.nn.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Collection; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JsonToNnTest extends AbstractBodyReaderTest { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractBodyReaderTest.class); + + private final JsonNormalizedNodeBodyReader jsonBodyReader; + private static EffectiveModelContext schemaContext; + + public JsonToNnTest() { + super(schemaContext, null); + this.jsonBodyReader = new JsonNormalizedNodeBodyReader(controllerContext); + } + + @BeforeClass + public static void initialize() throws FileNotFoundException { + final Collection<File> testFiles = TestRestconfUtils.loadFiles("/json-to-nn/simple-list-yang/1"); + testFiles.addAll(TestRestconfUtils.loadFiles("/json-to-nn/simple-list-yang/3")); + testFiles.addAll(TestRestconfUtils.loadFiles("/json-to-nn/simple-list-yang/4")); + testFiles.addAll(TestRestconfUtils.loadFiles("/json-to-nn/simple-container-yang")); + testFiles.addAll(TestRestconfUtils.loadFiles("/common/augment/yang")); + schemaContext = YangParserTestUtils.parseYangFiles(testFiles); + } + + @Test + public void simpleListTest() throws Exception { + simpleTest("/json-to-nn/simple-list.json", + "lst", "simple-list-yang1"); + } + + @Test + public void simpleContainerTest() throws Exception { + simpleTest("/json-to-nn/simple-container.json", + "cont", "simple-container-yang"); + } + + @Test + public void multipleItemsInLeafListTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + "/json-to-nn/multiple-leaflist-items.json", + "simple-list-yang1:lst"); + assertNotNull(normalizedNodeContext); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext + .getData()); + assertTrue(dataTree.contains("45")); + assertTrue(dataTree.contains("55")); + assertTrue(dataTree.contains("66")); + } + + @Test + public void multipleItemsInListTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + "/json-to-nn/multiple-items-in-list.json", + "multiple-items-yang:lst"); + assertNotNull(normalizedNodeContext); + + assertEquals("lst", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + verityMultipleItemsInList(normalizedNodeContext); + } + + @Test + public void nullArrayToSimpleNodeWithNullValueTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + "/json-to-nn/array-with-null.json", "array-with-null-yang:cont"); + assertNotNull(normalizedNodeContext); + + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("lf")); + assertTrue(dataTree.contains("empty")); + } + + @Test + public void incorrectTopLevelElementsTest() throws Exception { + mockBodyReader("simple-list-yang1:lst", this.jsonBodyReader, false); + + InputStream inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/wrong-top-level1.json"); + + int countExceptions = 0; + RestconfDocumentedException exception = null; + + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, + inputStream); + } catch (final RestconfDocumentedException e) { + exception = e; + countExceptions++; + } + assertNotNull(exception); + assertEquals( + "Error parsing input: Schema node with name wrong was not found under " + + "(urn:ietf:params:xml:ns:netconf:base:1.0)data.", + exception.getErrors().get(0).getErrorMessage()); + + inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/wrong-top-level2.json"); + exception = null; + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, + inputStream); + } catch (final RestconfDocumentedException e) { + exception = e; + countExceptions++; + } + assertNotNull(exception); + assertEquals( + "Error parsing input: Schema node with name lst1 was not found under " + + "(urn:ietf:params:xml:ns:netconf:base:1.0)data.", + exception.getErrors().get(0).getErrorMessage()); + + inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/wrong-top-level3.json"); + exception = null; + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, + inputStream); + } catch (final RestconfDocumentedException e) { + exception = e; + countExceptions++; + } + assertNotNull(exception); + assertEquals( + "Error parsing input: Schema node with name lf was not found under " + + "(urn:ietf:params:xml:ns:netconf:base:1.0)data.", + exception.getErrors().get(0).getErrorMessage()); + assertEquals(3, countExceptions); + } + + @Test + public void emptyDataReadTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + "/json-to-nn/empty-data.json", "array-with-null-yang:cont"); + assertNotNull(normalizedNodeContext); + + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + + assertTrue(dataTree.contains("lflst1")); + + assertTrue(dataTree.contains("lflst2 45")); + + RestconfDocumentedException exception = null; + mockBodyReader("array-with-null-yang:cont", this.jsonBodyReader, false); + final InputStream inputStream = this.getClass().getResourceAsStream("/json-to-nn/empty-data.json1"); + + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null,inputStream); + } catch (final RestconfDocumentedException e) { + exception = e; + } + assertNotNull(exception); + assertEquals("Error parsing input: null", exception.getErrors().get(0).getErrorMessage()); + } + + @Test + public void testJsonBlankInput() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC("", "array-with-null-yang:cont"); + assertNull(normalizedNodeContext); + } + + @Test + public void notSupplyNamespaceIfAlreadySupplied()throws Exception { + final String uri = "simple-list-yang1" + ":" + "lst"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC("/json-to-nn/simple-list.json", uri); + assertNotNull(normalizedNodeContext); + + verifyNormaluizedNodeContext(normalizedNodeContext, "lst"); + + mockBodyReader("simple-list-yang2:lst", this.jsonBodyReader, false); + final InputStream inputStream = this.getClass().getResourceAsStream("/json-to-nn/simple-list.json"); + + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream); + fail("NormalizedNodeContext should not be create because of different namespace"); + } catch (final RestconfDocumentedException e) { + LOG.warn("Read from InputStream failed. Message: {}. Status: {}", e.getMessage(), e.getStatus()); + } + + verifyNormaluizedNodeContext(normalizedNodeContext, "lst"); + } + + @Test + public void dataAugmentedTest() throws Exception { + NormalizedNodeContext normalizedNodeContext = prepareNNC("/common/augment/json/dataa.json", "main:cont"); + + assertNotNull(normalizedNodeContext); + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext + .getData()); + assertTrue(dataTree.contains("cont1")); + assertTrue(dataTree.contains("lf11 lf11 value from a")); + + normalizedNodeContext = prepareNNC("/common/augment/json/datab.json", "main:cont"); + + assertNotNull(normalizedNodeContext); + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("cont1")); + assertTrue(dataTree.contains("lf11 lf11 value from b")); + } + + private void simpleTest(final String jsonPath, final String topLevelElementName, + final String moduleName) throws Exception { + final String uri = moduleName + ":" + topLevelElementName; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC(jsonPath, uri); + assertNotNull(normalizedNodeContext); + + verifyNormaluizedNodeContext(normalizedNodeContext, topLevelElementName); + } + + private NormalizedNodeContext prepareNNC(final String jsonPath, final String uri) throws Exception { + try { + mockBodyReader(uri, this.jsonBodyReader, false); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + LOG.warn("Operation failed due to: {}", e.getMessage()); + } + final InputStream inputStream = this.getClass().getResourceAsStream(jsonPath); + + NormalizedNodeContext normalizedNodeContext = null; + + try { + normalizedNodeContext = this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream); + } catch (WebApplicationException e) { + // TODO Auto-generated catch block + } + + return normalizedNodeContext; + } + + private static void verifyNormaluizedNodeContext(final NormalizedNodeContext normalizedNodeContext, + final String topLevelElementName) { + assertEquals(topLevelElementName, normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("cont1")); + assertTrue(dataTree.contains("lst1")); + assertTrue(dataTree.contains("lflst1")); + assertTrue(dataTree.contains("lflst1_1")); + assertTrue(dataTree.contains("lflst1_2")); + assertTrue(dataTree.contains("lf1")); + } + + private static void verityMultipleItemsInList(final NormalizedNodeContext normalizedNodeContext) { + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("lf11")); + assertTrue(dataTree.contains("lf11_1")); + assertTrue(dataTree.contains("lflst11")); + assertTrue(dataTree.contains("45")); + assertTrue(dataTree.contains("cont11")); + assertTrue(dataTree.contains("lst11")); + } + + @Test + public void unsupportedDataFormatTest() throws Exception { + mockBodyReader("simple-list-yang1:lst", this.jsonBodyReader, false); + + final InputStream inputStream = this.getClass().getResourceAsStream("/json-to-nn/unsupported-json-format.json"); + + RestconfDocumentedException exception = null; + + try { + this.jsonBodyReader.readFrom(null, null, null, this.mediaType, null, inputStream); + } catch (final RestconfDocumentedException e) { + exception = e; + } + LOG.info(exception.getErrors().get(0).getErrorMessage()); + + assertTrue(exception.getErrors().get(0).getErrorMessage().contains("is not a simple type")); + } + + @Test + public void invalidUriCharacterInValue() throws Exception { + mockBodyReader("array-with-null-yang:cont", this.jsonBodyReader, false); + + final InputStream inputStream = this.getClass().getResourceAsStream( + "/json-to-nn/invalid-uri-character-in-value.json"); + + final NormalizedNodeContext normalizedNodeContext = this.jsonBodyReader.readFrom( + null, null, null, this.mediaType, null, inputStream); + assertNotNull(normalizedNodeContext); + + assertEquals("cont", normalizedNodeContext.getData().getIdentifier().getNodeType().getLocalName()); + + final String dataTree = NormalizedNodes.toStringTree(normalizedNodeContext.getData()); + assertTrue(dataTree.contains("lf1 module<Name:value lf1")); + assertTrue(dataTree.contains("lf2 module>Name:value lf2")); + } + + @Override + protected MediaType getMediaType() { + return null; + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnJsonChoiceCaseTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnJsonChoiceCaseTest.java new file mode 100644 index 0000000..8fd5db8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnJsonChoiceCaseTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.json.test; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class NnJsonChoiceCaseTest extends AbstractBodyReaderTest { + + private static EffectiveModelContext schemaContext; + private final NormalizedNodeJsonBodyWriter jsonBodyWriter; + + public NnJsonChoiceCaseTest() { + super(schemaContext, null); + jsonBodyWriter = new NormalizedNodeJsonBodyWriter(); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/nn-to-json/choice", schemaContext); + } + + /** + * Test when some data are in one case node and other in another. This isn't + * correct. Next Json validator should return error because nodes has to be + * from one case below concrete choice. + */ + @Test(expected = NullPointerException.class) + public void nodeSchemasOnVariousChoiceCasePathTest() throws Exception { + getJson("/nn-to-json/choice/xml/data_various_path_err.xml"); + } + + /** + * Test when some data are in one case node and other in another. + * Additionally data are loadef from various choices. This isn't correct. + * Next Json validator should return error because nodes has to be from one + * case below concrete choice. + */ + @Test(expected = NullPointerException.class) + public void nodeSchemasOnVariousChoiceCasePathAndMultipleChoicesTest() + throws Exception { + getJson("/nn-to-json/choice/xml/data_more_choices_same_level_various_paths_err.xml"); + } + + /** + * Test when second level data are red first, then first and at the end + * third level. Level represents pass through couple choice-case + */ + + @Test + public void nodeSchemasWithRandomOrderAccordingLevel() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_random_level.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lf1\":\"lf1 val\"")); + assertTrue(json.contains("\"lf1aaa\":\"lf1aaa val\"")); + assertTrue(json.contains("\"lf1aa\":\"lf1aa val\"")); + assertTrue(json.contains("\"lf1a\":121")); + } + + /** + * Test when element from no first case is used. + */ + @Test + public void nodeSchemasNotInFirstCase() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_no_first_case.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lf1\":\"lf1 val\"")); + assertTrue(json.contains("\"lf1ab\":\"lf1ab val\"")); + assertTrue(json.contains("\"lf1a\":121")); + } + + /** + * Test when element in case is list. + */ + @Test + public void nodeSchemaAsList() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_list.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lst1b\":[")); + assertTrue(json.contains("{\"lf11b\":\"lf11b_1 val\"}")); + assertTrue(json.contains("{\"lf11b\":\"lf11b_2 val\"}")); + } + + /** + * Test when element in case is container. + */ + @Test + public void nodeSchemaAsContainer() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_container.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"cont1c\":{")); + assertTrue(json.contains("\"lf11c\":\"lf11c val\"")); + } + + /** + * Test when element in case is leaflist. + */ + @Test + public void nodeSchemaAsLeafList() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_leaflist.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lflst1d\":[")); + assertTrue(json.contains("\"lflst1d_1 val\"")); + assertTrue(json.contains("\"lflst1d_2 val\"")); + } + + @Test + public void nodeSchemasInMultipleChoicesTest() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_more_choices_same_level.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lf2b\":\"lf2b value\"")); + assertTrue(json.contains("\"cont1c\":{")); + assertTrue(json.contains("\"lf11c\":\"lf11c val\"")); + } + + /** + * Test whether is possible to find data schema for node which is specified + * as dirrect subnode of choice (case without CASE key word). + */ + @Test + public void nodeSchemasInCaseNotDefinedWithCaseKeyword() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_case_defined_without_case.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("\"lf2b\":\"lf2b val\"")); + assertTrue(json.contains("\"e1\":45")); + } + + /** + * Test of multiple use of choices. + */ + @Test + public void nodeSchemasInThreeChoicesAtSameLevel() throws Exception { + final String json = getJson("/nn-to-json/choice/xml/data_three_choices_same_level.xml"); + + assertTrue(json.contains("cont")); + assertTrue(json.contains("lf2b\":\"lf2b value")); + assertTrue(json.contains("lst4a\":[{")); + assertTrue(json.contains("{\"lf4ab\":33}")); + assertTrue(json.contains("{\"lf4ab\":37}")); + assertTrue(json.contains("\"lf1aaa\":\"lf1aaa value\"")); + } + + private String getJson(final String xmlPath) throws Exception { + final String uri = "choice-case-test:cont"; + final NormalizedNodeContext testNN = TestRestconfUtils + .loadNormalizedContextFromXmlFile(xmlPath, uri, controllerContext); + + final OutputStream output = new ByteArrayOutputStream(); + jsonBodyWriter.writeTo(testNN, null, null, null, mediaType, null, + output); + + return output.toString(); + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonLeafrefType.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonLeafrefType.java new file mode 100644 index 0000000..4ea6130 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonLeafrefType.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.json.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class NnToJsonLeafrefType extends AbstractBodyReaderTest { + + private static EffectiveModelContext schemaContext; + private final NormalizedNodeJsonBodyWriter jsonBodyWriter; + + public NnToJsonLeafrefType() { + super(schemaContext, null); + jsonBodyWriter = new NormalizedNodeJsonBodyWriter(); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/nn-to-json/leafref", schemaContext); + } + + @Test + public void leafrefAbsolutePathToExistingLeafTest() throws Exception { + final String json = toJson("/nn-to-json/leafref/xml/data_absolut_ref_to_existing_leaf.xml"); + validateJson(".*\"lf3\":\\p{Blank}*\"true\".*", json); + } + + @Test + public void leafrefRelativePathToExistingLeafTest() throws Exception { + final String json = toJson("/nn-to-json/leafref/xml/data_relativ_ref_to_existing_leaf.xml"); + validateJson(".*\"lf2\":\\p{Blank}*\"121\".*", json); + } + + @Test(expected = NullPointerException.class) + public void leafrefToNonExistingLeafTest() throws Exception { + toJson("/nn-to-json/leafref/xml/data_ref_to_non_existing_leaf.xml"); + } + + @Test + public void leafrefToNotLeafTest() throws Exception { + final String json = toJson("/nn-to-json/leafref/xml/data_ref_to_not_leaf.xml"); + validateJson( + ".*\"cont-augment-module\\p{Blank}*:\\p{Blank}*lf6\":\\p{Blank}*\"44\".*", + json); + } + + @Test + public void leafrefFromLeafListToLeafTest() throws Exception { + final String json = toJson("/nn-to-json/leafref/xml/data_relativ_ref_from_leaflist_to_existing_leaf.xml"); + validateJson(".*\"cont-augment-module\\p{Blank}*:\\p{Blank}*lflst1\":\\p{Blank}*.*\"34[5|6|7]\",*\"34[5|6|7]\"," + + "*\"34[5|6|7]\".*", json); + } + + @Test + public void leafrefFromLeafrefToLeafrefTest() throws Exception { + final String json = toJson("/nn-to-json/leafref/xml/data_from_leafref_to_leafref.xml"); + validateJson( + ".*\"cont-augment-module\\p{Blank}*:\\p{Blank}*lf7\":\\p{Blank}*\"200\".*", + json); + } + + private static void validateJson(final String regex, final String value) { + assertNotNull(value); + final Pattern ptrn = Pattern.compile(regex, Pattern.DOTALL); + final Matcher mtch = ptrn.matcher(value); + assertTrue(mtch.matches()); + } + + private String toJson(final String xmlDataPath) throws Exception { + final String uri = "main-module:cont"; + final String pathToInputFile = xmlDataPath; + + final NormalizedNodeContext testNN = TestRestconfUtils + .loadNormalizedContextFromXmlFile(pathToInputFile, uri, controllerContext); + + final OutputStream output = new ByteArrayOutputStream(); + jsonBodyWriter.writeTo(testNN, null, null, null, mediaType, null, + output); + final String jsonOutput = output.toString(); + + return jsonOutput; + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonWithAugmentTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonWithAugmentTest.java new file mode 100644 index 0000000..d97b246 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/json/test/NnToJsonWithAugmentTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.json.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +@Deprecated +public class NnToJsonWithAugmentTest extends AbstractBodyReaderTest { + + private static EffectiveModelContext schemaContext; + private final NormalizedNodeJsonBodyWriter xmlBodyWriter; + + public NnToJsonWithAugmentTest() { + super(schemaContext, null); + xmlBodyWriter = new NormalizedNodeJsonBodyWriter(); + } + + @BeforeClass + public static void initialize() { + schemaContext = schemaContextLoader("/nn-to-json/augmentation", schemaContext); + } + + @Test + public void augmentedElementsToJson() throws WebApplicationException, + IOException { + final String uri = "yang:cont"; + final String pathToInputFile = "/nn-to-json/augmentation/xml/data.xml"; + + final NormalizedNodeContext testNN = TestRestconfUtils + .loadNormalizedContextFromXmlFile(pathToInputFile, uri, controllerContext); + + final OutputStream output = new ByteArrayOutputStream(); + xmlBodyWriter + .writeTo(testNN, null, null, null, mediaType, null, output); + final String jsonOutput = output.toString(); + + assertNotNull(jsonOutput); + assertTrue(jsonOutput.contains("\"cont1\"" + ":" + '{')); + assertTrue(jsonOutput.contains("\"lf11\"" + ":" + "\"lf11\"")); + assertTrue(jsonOutput.contains("\"lst1\"" + ":" + '[')); + assertTrue(jsonOutput.contains("\"lf11\"" + ":" + "\"lf1_1\"")); + assertTrue(jsonOutput.contains("\"lf11\"" + ":" + "\"lf1_2\"")); + assertTrue(jsonOutput.contains("\"lflst1\"" + ":" + "[")); + assertTrue(jsonOutput.contains("\"lf2\"" + ":" + "\"lf2\"")); + } + + @Override + protected MediaType getMediaType() { + return new MediaType(MediaType.APPLICATION_XML, null); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnInstanceIdentifierToXmlTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnInstanceIdentifierToXmlTest.java new file mode 100644 index 0000000..2ab6476 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnInstanceIdentifierToXmlTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.xml.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.net.URISyntaxException; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +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.util.SchemaInferenceStack; + +public class NnInstanceIdentifierToXmlTest extends AbstractBodyReaderTest { + private static EffectiveModelContext schemaContext; + + private final NormalizedNodeXmlBodyWriter xmlBodyWriter = new NormalizedNodeXmlBodyWriter(); + + public NnInstanceIdentifierToXmlTest() { + super(schemaContext, null); + } + + @BeforeClass + public static void initialization() throws URISyntaxException { + schemaContext = schemaContextLoader("/instanceidentifier/yang", schemaContext); + } + + @Test + public void nnAsYangInstanceIdentifierAugmentLeafList() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNCLeafList(); + + final OutputStream output = new ByteArrayOutputStream(); + + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, mediaType, null, output); + + assertNotNull(output); + + final String outputJson = output.toString(); + + assertTrue(outputJson.contains("<cont xmlns=")); + assertTrue(outputJson.contains( + '"' + "instance:identifier:module" + '"')); + assertTrue(outputJson.contains(">")); + + assertTrue(outputJson.contains("<cont1>")); + + assertTrue(outputJson.contains("<lf11 xmlns=")); + assertTrue(outputJson.contains( + '"' + "augment:module:leaf:list" + '"')); + assertTrue(outputJson.contains(">")); + assertTrue(outputJson.contains("/instanceidentifier/")); + assertTrue(outputJson.contains("</lf11>")); + + assertTrue(outputJson.contains("<lflst11 xmlns=")); + assertTrue(outputJson.contains( + '"' + "augment:module:leaf:list" + '"')); + assertTrue(outputJson.contains(">")); + assertTrue(outputJson.contains("lflst11 value")); + assertTrue(outputJson.contains("</lflst11>")); + + assertTrue(outputJson.contains("</cont1>")); + assertTrue(outputJson.contains("</cont>")); + } + + private static NormalizedNodeContext prepareNNCLeafList() throws URISyntaxException { + final QName cont = QName.create("instance:identifier:module", "2014-01-17", + "cont"); + final QName cont1 = QName.create("instance:identifier:module", "2014-01-17", + "cont1"); + final QName lflst11 = QName.create("augment:module:leaf:list", "2014-01-17", + "lflst11"); + final QName lf11 = QName.create("augment:module:leaf:list", "2014-01-17", + "lf11"); + + final DataSchemaNode schemaCont = schemaContext.getDataChildByName(cont); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataCont = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) schemaCont); + + final DataSchemaNode schemaCont1 = ((ContainerSchemaNode) schemaCont).getDataChildByName(cont1); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataCont1 = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) schemaCont1); + + final var instanceLfLst11 = ControllerContext.findInstanceDataChildrenByName( + (DataNodeContainer) schemaCont1, lflst11.getLocalName()); + + final DataSchemaNode lfLst11Schema = instanceLfLst11.get(0).child; + final ListNodeBuilder<Object, SystemLeafSetNode<Object>> lfLst11Data = SchemaAwareBuilders + .leafSetBuilder((LeafListSchemaNode) lfLst11Schema); + + lfLst11Data.withChild(SchemaAwareBuilders.leafSetEntryBuilder((LeafListSchemaNode) lfLst11Schema) + .withValue("lflst11 value").build()); + dataCont1.withChild(lfLst11Data.build()); + + final var instanceLf11 = ControllerContext.findInstanceDataChildrenByName( + (DataNodeContainer) schemaCont1, lf11.getLocalName()); + final DataSchemaNode lf11Schema = instanceLf11.get(0).child; + + dataCont1.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) lf11Schema) + .withValue("/instanceidentifier/").build()); + dataCont.withChild(dataCont1.build()); + + return new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + dataCont.build()); + } + + @Test + public void nnAsYangInstanceIdentifierAugment() throws Exception { + + final NormalizedNodeContext normalizedNodeContext = preparNNC(); + final OutputStream output = new ByteArrayOutputStream(); + + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, + mediaType, null, output); + + assertNotNull(output); + + final String outputJson = output.toString(); + + assertTrue(outputJson.contains("<cont xmlns=")); + assertTrue(outputJson.contains( + '"' + "instance:identifier:module" + '"')); + assertTrue(outputJson.contains(">")); + + assertTrue(outputJson.contains("<cont1>")); + + assertTrue(outputJson.contains("<lst11 xmlns=")); + assertTrue(outputJson.contains('"' + "augment:module" + '"')); + assertTrue(outputJson.contains(">")); + + assertTrue(outputJson.contains( + "<keyvalue111>keyvalue111</keyvalue111>")); + assertTrue(outputJson.contains( + "<keyvalue112>keyvalue112</keyvalue112>")); + + assertTrue(outputJson.contains("<lf111 xmlns=")); + assertTrue(outputJson.contains( + '"' + "augment:augment:module" + '"')); + assertTrue(outputJson.contains(">/cont/cont1/lf12</lf111>")); + + assertTrue(outputJson.contains("<lf112 xmlns=")); + assertTrue(outputJson.contains( + '"' + "augment:augment:module" + '"')); + assertTrue(outputJson.contains(">lf12 value</lf112>")); + + assertTrue(outputJson.contains("</lst11></cont1></cont>")); + } + + private static NormalizedNodeContext preparNNC() { + final QName cont = QName.create("instance:identifier:module", "2014-01-17", + "cont"); + final QName cont1 = QName.create("instance:identifier:module", "2014-01-17", + "cont1"); + final QName lst11 = QName.create("augment:module", "2014-01-17", "lst11"); + final QName lf11 = QName.create("augment:augment:module", "2014-01-17", + "lf111"); + final QName lf12 = QName.create("augment:augment:module", "2014-01-17", + "lf112"); + final QName keyvalue111 = QName.create("augment:module", "2014-01-17", + "keyvalue111"); + final QName keyvalue112 = QName.create("augment:module", "2014-01-17", + "keyvalue112"); + + final DataSchemaNode schemaCont = schemaContext.getDataChildByName(cont); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataCont = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) schemaCont); + + final DataSchemaNode schemaCont1 = ((ContainerSchemaNode) schemaCont) + .getDataChildByName(cont1); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataCont1 = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) schemaCont1); + + final var instanceLst11 = ControllerContext.findInstanceDataChildrenByName( + (DataNodeContainer) schemaCont1, lst11.getLocalName()); + final DataSchemaNode lst11Schema = instanceLst11.get(0).child; + + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> dataLst11 = SchemaAwareBuilders + .mapBuilder((ListSchemaNode) lst11Schema); + + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> dataLst11Vaule = SchemaAwareBuilders + .mapEntryBuilder((ListSchemaNode) lst11Schema); + + dataLst11Vaule.withChild(buildLeaf(lst11Schema, keyvalue111, dataLst11, "keyvalue111")); + + dataLst11Vaule.withChild(buildLeaf(lst11Schema, keyvalue112, dataLst11, "keyvalue112")); + + dataLst11Vaule.withChild(buildLeaf(lst11Schema, lf11, dataLst11, "/cont/cont1/lf12")); + + dataLst11Vaule.withChild(buildLeaf(lst11Schema, lf12, dataLst11, "lf12 value")); + + dataLst11.withChild(dataLst11Vaule.build()); + + dataCont1.withChild(dataLst11.build()); + dataCont.withChild(dataCont1.build()); + + return new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + dataCont.build()); + } + + private static DataContainerChild buildLeaf(final DataSchemaNode lst11Schema, final QName qname, + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> dataLst11, final Object value) { + + final var instanceLf = ControllerContext.findInstanceDataChildrenByName( + (DataNodeContainer) lst11Schema, qname.getLocalName()); + final DataSchemaNode schemaLf = instanceLf.get(0).child; + + return SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue(value).build(); + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlTest.java new file mode 100644 index 0000000..b1385d9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlTest.java @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.xml.test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.google.common.base.Throwables; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Uint32; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition; +import org.opendaylight.yangtools.yang.model.ri.type.BaseTypes; +import org.opendaylight.yangtools.yang.model.ri.type.BitsTypeBuilder; +import org.opendaylight.yangtools.yang.model.ri.type.EnumerationTypeBuilder; +import org.opendaylight.yangtools.yang.model.ri.type.UnionTypeBuilder; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +public class NnToXmlTest extends AbstractBodyReaderTest { + private static EffectiveModelContext schemaContext; + + private final NormalizedNodeXmlBodyWriter xmlBodyWriter; + + public NnToXmlTest() { + super(schemaContext, null); + xmlBodyWriter = new NormalizedNodeXmlBodyWriter(); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/nn-to-xml/yang", schemaContext); + } + + @Test + public void nnAsYangIdentityrefToXMLTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareIdrefData(null, true); + nnToXml(normalizedNodeContext, "<lf11 xmlns:x=\"referenced:module\">x:iden</lf11>"); + } + + @Test + public void nnAsYangIdentityrefWithQNamePrefixToXMLTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareIdrefData("prefix", true); + nnToXml(normalizedNodeContext, "<lf11 xmlns", "=\"referenced:module\">", ":iden</lf11>"); + } + + @Test + public void nnAsYangIdentityrefWithPrefixToXMLTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareIdrefData("prefix", false); + nnToXml(normalizedNodeContext, "<lf11>no qname value</lf11>"); + } + + @Test + public void nnAsYangLeafrefWithPrefixToXMLTest() throws Exception { + nnToXml(prepareLeafrefData(), "<lfBoolean>true</lfBoolean>", "<lfLfref>true</lfLfref>"); + } + + /** + * Negative test when leaf of type leafref references to not-existing leaf. + * {@code VerifyException} is expected. + */ + @Test + public void nnAsYangLeafrefWithPrefixToXMLNegativeTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareLeafrefNegativeData(); + + final IOException ex = assertThrows(IOException.class, () -> nnToXml(normalizedNodeContext, + "<not-existing>value</not-existing>", "<lfLfrefNegative>value</lfLfrefnegative>")); + final Throwable rootCause = Throwables.getRootCause(ex); + assertThat(rootCause, instanceOf(IllegalArgumentException.class)); + assertEquals("Data tree child (basic:module?revision=2013-12-02)not-existing not present in schema parent " + + "(basic:module?revision=2013-12-02)cont", rootCause.getMessage()); + } + + @Test + public void nnAsYangStringToXmlTest() throws Exception { + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.stringType()).deserialize("lfStr value"), "lfStr"); + nnToXml(normalizedNodeContext, "<lfStr>lfStr value</lfStr>"); + } + + @Test + public void nnAsYangInt8ToXmlTest() throws Exception { + final String elName = "lfInt8"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.int8Type()).deserialize("14"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">14</" + elName + ">"); + } + + @Test + public void nnAsYangInt16ToXmlTest() throws Exception { + final String elName = "lfInt16"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.int16Type()).deserialize("3000"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">3000</" + elName + ">"); + } + + @Test + public void nnAsYangInt32ToXmlTest() throws Exception { + final String elName = "lfInt32"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.int32Type()).deserialize("201234"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">201234</" + elName + ">"); + } + + @Test + public void nnAsYangInt64ToXmlTest() throws Exception { + final String elName = "lfInt64"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.int64Type()).deserialize("5123456789"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">5123456789</" + elName + ">"); + } + + @Test + public void nnAsYangUint8ToXmlTest() throws Exception { + final String elName = "lfUint8"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.uint8Type()).deserialize("200"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">200</" + elName + ">"); + } + + @Test + public void snAsYangUint16ToXmlTest() throws Exception { + final String elName = "lfUint16"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.uint16Type()).deserialize("4000"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">4000</" + elName + ">"); + } + + @Test + public void nnAsYangUint32ToXmlTest() throws Exception { + final String elName = "lfUint32"; + + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.uint32Type()).deserialize("4123456789"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">4123456789</" + elName + ">"); + } + + @Test + public void snAsYangUint64ToXmlTest() throws Exception { + final String elName = "lfUint64"; + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.uint64Type()).deserialize("5123456789"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">5123456789</" + elName + ">"); + } + + @Test + public void nnAsYangBinaryToXmlTest() throws Exception { + final String elName = "lfBinary"; + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.binaryType()) + .deserialize("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567"), + elName); + nnToXml(normalizedNodeContext, + "<" + elName + ">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567</" + elName + ">"); + } + + @Test + public void nnAsYangBitsToXmlTest() throws Exception { + final BitsTypeDefinition.Bit mockBit1 = Mockito.mock(BitsTypeDefinition.Bit.class); + Mockito.when(mockBit1.getName()).thenReturn("one"); + Mockito.when(mockBit1.getPosition()).thenReturn(Uint32.ONE); + final BitsTypeDefinition.Bit mockBit2 = Mockito.mock(BitsTypeDefinition.Bit.class); + Mockito.when(mockBit2.getName()).thenReturn("two"); + Mockito.when(mockBit2.getPosition()).thenReturn(Uint32.TWO); + final BitsTypeBuilder bitsTypeBuilder = BaseTypes.bitsTypeBuilder(QName.create("foo", "foo")); + bitsTypeBuilder.addBit(mockBit1); + bitsTypeBuilder.addBit(mockBit2); + + final String elName = "lfBits"; + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(bitsTypeBuilder.build()).deserialize("one two"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">one two</" + elName + ">"); + } + + @Test + public void nnAsYangEnumerationToXmlTest() throws Exception { + final EnumTypeDefinition.EnumPair mockEnum = Mockito.mock(EnumTypeDefinition.EnumPair.class); + Mockito.when(mockEnum.getName()).thenReturn("enum2"); + + final EnumerationTypeBuilder enumerationTypeBuilder = BaseTypes + .enumerationTypeBuilder(QName.create("foo", "foo")); + enumerationTypeBuilder.addEnum(mockEnum); + + final String elName = "lfEnumeration"; + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(enumerationTypeBuilder.build()).deserialize("enum2"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">enum2</" + elName + ">"); + } + + @Test + public void nnAsYangEmptyToXmlTest() throws Exception { + final String elName = "lfEmpty"; + final NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.emptyType()).deserialize(""), elName); + nnToXml(normalizedNodeContext, "<" + elName + "/>"); + } + + @Test + public void nnAsYangBooleanToXmlTest() throws Exception { + final String elName = "lfBoolean"; + NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(BaseTypes.booleanType()).deserialize("false"), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">false</" + elName + ">"); + + normalizedNodeContext = prepareNNC(TypeDefinitionAwareCodec.from(BaseTypes.booleanType()).deserialize("true"), + elName); + nnToXml(normalizedNodeContext, "<" + elName + ">true</" + elName + ">"); + } + + @Test + public void nnAsYangUnionToXmlTest() throws Exception { + final BitsTypeDefinition.Bit mockBit1 = Mockito.mock(BitsTypeDefinition.Bit.class); + Mockito.when(mockBit1.getName()).thenReturn("first"); + Mockito.when(mockBit1.getPosition()).thenReturn(Uint32.ONE); + final BitsTypeDefinition.Bit mockBit2 = Mockito.mock(BitsTypeDefinition.Bit.class); + Mockito.when(mockBit2.getName()).thenReturn("second"); + Mockito.when(mockBit2.getPosition()).thenReturn(Uint32.TWO); + + final BitsTypeBuilder bitsTypeBuilder = BaseTypes.bitsTypeBuilder(QName.create("foo", "foo")); + bitsTypeBuilder.addBit(mockBit1); + bitsTypeBuilder.addBit(mockBit2); + + final UnionTypeBuilder unionTypeBuilder = BaseTypes.unionTypeBuilder(QName.create("foo", "foo")); + unionTypeBuilder.addType(BaseTypes.int8Type()); + unionTypeBuilder.addType(bitsTypeBuilder.build()); + unionTypeBuilder.addType(BaseTypes.booleanType()); + unionTypeBuilder.addType(BaseTypes.stringType()); + + final String elName = "lfUnion"; + + // test int8 + final String int8 = "15"; + NormalizedNodeContext normalizedNodeContext = prepareNNC( + TypeDefinitionAwareCodec.from(unionTypeBuilder.build()).deserialize(int8), elName); + nnToXml(normalizedNodeContext, "<" + elName + ">15</" + elName + ">"); + + // test bits + final String bits = "first second"; + normalizedNodeContext = prepareNNC(TypeDefinitionAwareCodec.from(unionTypeBuilder.build()).deserialize(bits), + elName); + nnToXml(normalizedNodeContext, "<" + elName + ">[first, second]</" + elName + ">"); + + // test boolean + final String bool = "true"; + normalizedNodeContext = prepareNNC(TypeDefinitionAwareCodec.from(unionTypeBuilder.build()).deserialize(bool), + elName); + nnToXml(normalizedNodeContext, "<" + elName + ">true</" + elName + ">"); + + // test string + final String s = "Hi!"; + normalizedNodeContext = prepareNNC(TypeDefinitionAwareCodec.from(unionTypeBuilder.build()).deserialize(s), + elName); + nnToXml(normalizedNodeContext, "<" + elName + ">Hi!</" + elName + ">"); + } + + private static NormalizedNodeContext prepareNNC(final Object object, final String name) { + final QName cont = QName.create("basic:module", "2013-12-02", "cont"); + final QName lf = QName.create("basic:module", "2013-12-02", name); + + final DataSchemaNode contSchema = schemaContext.getDataChildByName(cont); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contData = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchema); + + final var instanceLf = ControllerContext + .findInstanceDataChildrenByName((DataNodeContainer) contSchema, lf.getLocalName()); + final DataSchemaNode schemaLf = instanceLf.get(0).child; + + contData.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue(object).build()); + + return new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + contData.build()); + } + + private void nnToXml(final NormalizedNodeContext normalizedNodeContext, final String... xmlRepresentation) + throws Exception { + final OutputStream output = new ByteArrayOutputStream(); + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, mediaType, null, output); + + for (String element : xmlRepresentation) { + assertTrue(output.toString().contains(element)); + } + } + + private static NormalizedNodeContext prepareLeafrefData() { + final QName cont = QName.create("basic:module", "2013-12-02", "cont"); + final QName lfBoolean = QName.create("basic:module", "2013-12-02", "lfBoolean"); + final QName lfLfref = QName.create("basic:module", "2013-12-02", "lfLfref"); + + final DataSchemaNode contSchema = schemaContext.getDataChildByName(cont); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contData = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchema); + + var instanceLf = ControllerContext + .findInstanceDataChildrenByName((DataNodeContainer) contSchema, lfBoolean.getLocalName()); + DataSchemaNode schemaLf = instanceLf.get(0).child; + + contData.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue(Boolean.TRUE).build()); + + instanceLf = ControllerContext.findInstanceDataChildrenByName((DataNodeContainer) contSchema, + lfLfref.getLocalName()); + schemaLf = instanceLf.get(0).child; + + contData.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue("true").build()); + + return new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + contData.build()); + } + + private static NormalizedNodeContext prepareLeafrefNegativeData() { + final QName cont = QName.create("basic:module", "2013-12-02", "cont"); + final QName lfLfref = QName.create("basic:module", "2013-12-02", "lfLfrefNegative"); + + final DataSchemaNode contSchema = schemaContext.getDataChildByName(cont); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contData = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchema); + + final var instanceLf = ControllerContext.findInstanceDataChildrenByName((DataNodeContainer) + contSchema, lfLfref.getLocalName()); + final DataSchemaNode schemaLf = instanceLf.get(0).child; + + contData.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue("value").build()); + + return new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + contData.build()); + } + + private static NormalizedNodeContext prepareIdrefData(final String prefix, final boolean valueAsQName) { + final QName cont = QName.create("basic:module", "2013-12-02", "cont"); + final QName cont1 = QName.create("basic:module", "2013-12-02", "cont1"); + final QName lf11 = QName.create("basic:module", "2013-12-02", "lf11"); + + final DataSchemaNode contSchema = schemaContext.getDataChildByName(cont); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contData = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchema); + + final DataSchemaNode cont1Schema = ((ContainerSchemaNode) contSchema).getDataChildByName(cont1); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> cont1Data = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) cont1Schema); + + Object value = null; + if (valueAsQName) { + value = QName.create("referenced:module", "2013-12-02", "iden"); + } else { + value = "no qname value"; + } + + final var instanceLf = ControllerContext + .findInstanceDataChildrenByName((DataNodeContainer) cont1Schema, lf11.getLocalName()); + final DataSchemaNode schemaLf = instanceLf.get(0).child; + + cont1Data.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf).withValue(value).build()); + + contData.withChild(cont1Data.build()); + + final NormalizedNodeContext testNormalizedNodeContext = new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, cont)), + contData.build()); + return testNormalizedNodeContext; + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithChoiceTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithChoiceTest.java new file mode 100644 index 0000000..9d2954d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithChoiceTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.xml.test; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +public class NnToXmlWithChoiceTest extends AbstractBodyReaderTest { + + private final NormalizedNodeXmlBodyWriter xmlBodyWriter; + private static EffectiveModelContext schemaContext; + + public NnToXmlWithChoiceTest() { + super(schemaContext, null); + xmlBodyWriter = new NormalizedNodeXmlBodyWriter(); + } + + @BeforeClass + public static void initialization() { + schemaContext = schemaContextLoader("/nn-to-xml/choice", schemaContext); + } + + @Test + public void cnSnToXmlWithYangChoice() throws Exception { + NormalizedNodeContext normalizedNodeContext = prepareNNC("lf1", + "String data1"); + OutputStream output = new ByteArrayOutputStream(); + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, + mediaType, null, output); + assertTrue(output.toString().contains("<lf1>String data1</lf1>")); + + normalizedNodeContext = prepareNNC("lf2", "String data2"); + output = new ByteArrayOutputStream(); + + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, + mediaType, null, output); + assertTrue(output.toString().contains("<lf2>String data2</lf2>")); + } + + private static NormalizedNodeContext prepareNNC(final String name, final Object value) { + + final QName contQname = QName.create("module:with:choice", "2013-12-18", + "cont"); + final QName lf = QName.create("module:with:choice", "2013-12-18", name); + final QName choA = QName.create("module:with:choice", "2013-12-18", "choA"); + + final DataSchemaNode contSchemaNode = schemaContext + .getDataChildByName(contQname); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataContainerNodeAttrBuilder = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchemaNode); + + final DataSchemaNode choiceSchemaNode = ((ContainerSchemaNode) contSchemaNode) + .getDataChildByName(choA); + assertTrue(choiceSchemaNode instanceof ChoiceSchemaNode); + + final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> dataChoice = SchemaAwareBuilders + .choiceBuilder((ChoiceSchemaNode) choiceSchemaNode); + + final var instanceLf = ControllerContext + .findInstanceDataChildrenByName( + (DataNodeContainer) contSchemaNode, lf.getLocalName()); + final DataSchemaNode schemaLf = instanceLf.get(0).child; + + dataChoice.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) schemaLf) + .withValue(value).build()); + + dataContainerNodeAttrBuilder.withChild(dataChoice.build()); + + final NormalizedNodeContext testNormalizedNodeContext = new NormalizedNodeContext( + InstanceIdentifierContext.ofStack(SchemaInferenceStack.ofDataTreePath(schemaContext, contQname)), + dataContainerNodeAttrBuilder.build()); + + return testNormalizedNodeContext; + } + + @Override + protected MediaType getMediaType() { + return null; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithDataFromSeveralModulesTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithDataFromSeveralModulesTest.java new file mode 100644 index 0000000..a9cf7a6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/nn/to/xml/test/NnToXmlWithDataFromSeveralModulesTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.nn.to.xml.test; + +import static org.junit.Assert.assertTrue; + +import com.google.common.base.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URISyntaxException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.sal.rest.impl.test.providers.AbstractBodyReaderTest; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; + +public class NnToXmlWithDataFromSeveralModulesTest extends + AbstractBodyReaderTest { + + private final NormalizedNodeXmlBodyWriter xmlBodyWriter; + private static EffectiveModelContext schemaContext; + + public NnToXmlWithDataFromSeveralModulesTest() { + super(schemaContext, null); + xmlBodyWriter = new NormalizedNodeXmlBodyWriter(); + } + + @BeforeClass + public static void initialize() { + schemaContext = schemaContextLoader("/nn-to-xml/data-of-several-modules/yang", schemaContext); + } + + @Test + public void dataFromSeveralModulesToXmlTest() + throws WebApplicationException, IOException, URISyntaxException { + final NormalizedNodeContext normalizedNodeContext = prepareNormalizedNodeContext(); + final OutputStream output = new ByteArrayOutputStream(); + xmlBodyWriter.writeTo(normalizedNodeContext, null, null, null, + mediaType, null, output); + + final String outputString = output.toString(); + // data + assertTrue(outputString + .contains( + "<data xmlns=" + '"' + + "urn:ietf:params:xml:ns:netconf:base:1.0" + + '"' + '>')); + // cont m2 + assertTrue(outputString.contains( + "<cont_m2 xmlns=" + '"' + "module:two" + '"' + '>')); + assertTrue(outputString.contains("<lf1_m2>lf1 m2 value</lf1_m2>")); + assertTrue(outputString.contains("<contB_m2/>")); + assertTrue(outputString.contains("</cont_m2>")); + + // cont m1 + assertTrue(outputString.contains( + "<cont_m1 xmlns=" + '"' + "module:one" + '"' + '>')); + assertTrue(outputString.contains("<contB_m1/>")); + assertTrue(outputString.contains("<lf1_m1>lf1 m1 value</lf1_m1>")); + assertTrue(outputString.contains("</cont_m1>")); + + // end + assertTrue(output.toString().contains("</data>")); + } + + @Override + protected MediaType getMediaType() { + // TODO Auto-generated method stub + return null; + } + + private static NormalizedNodeContext prepareNormalizedNodeContext() { + final String rev = "2014-01-17"; + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataContSchemaContNode = SchemaAwareBuilders + .containerBuilder(schemaContext); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> modul1 = buildContBuilderMod1( + "module:one", rev, "cont_m1", "contB_m1", "lf1_m1", + "lf1 m1 value"); + dataContSchemaContNode.withChild(modul1.build()); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> modul2 = buildContBuilderMod1( + "module:two", rev, "cont_m2", "contB_m2", "lf1_m2", + "lf1 m2 value"); + dataContSchemaContNode.withChild(modul2.build()); + + final NormalizedNodeContext testNormalizedNodeContext = new NormalizedNodeContext( + InstanceIdentifierContext.ofLocalRoot(schemaContext), + dataContSchemaContNode.build()); + + return testNormalizedNodeContext; + } + + private static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> buildContBuilderMod1( + final String uri, final String rev, final String cont, final String contB, final String lf1, + final String lf1Value) { + final QName contQname = QName.create(uri, rev, cont); + final QName contBQname = QName.create(uri, rev, contB); + final QName lf1Qname = QName.create(contQname, lf1); + + final DataSchemaNode contSchemaNode = schemaContext + .getDataChildByName(contQname); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataContainerNodeAttrBuilder = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contSchemaNode); + + Preconditions.checkState(contSchemaNode instanceof ContainerSchemaNode); + final var instanceLf1_m1 = ControllerContext.findInstanceDataChildrenByName( + (DataNodeContainer) contSchemaNode, lf1Qname.getLocalName()); + final DataSchemaNode schemaLf1_m1 = instanceLf1_m1.get(0).child; + + dataContainerNodeAttrBuilder.withChild(SchemaAwareBuilders + .leafBuilder((LeafSchemaNode) schemaLf1_m1) + .withValue(lf1Value).build()); + + final DataSchemaNode contBSchemaNode = ((ContainerSchemaNode) contSchemaNode) + .getDataChildByName(contBQname); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> dataContainerB = SchemaAwareBuilders + .containerBuilder((ContainerSchemaNode) contBSchemaNode); + + return dataContainerNodeAttrBuilder.withChild(dataContainerB.build()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bierman02RestConfWiringTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bierman02RestConfWiringTest.java new file mode 100644 index 0000000..990453f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bierman02RestConfWiringTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; + +import com.google.inject.AbstractModule; +import java.io.IOException; +import java.net.URISyntaxException; +import javax.inject.Inject; +import org.junit.Rule; +import org.junit.Test; +import org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration; +import org.opendaylight.aaa.web.WebServer; +import org.opendaylight.aaa.web.testutils.TestWebClient; +import org.opendaylight.aaa.web.testutils.WebTestModule; +import org.opendaylight.controller.sal.restconf.impl.test.incubate.InMemoryMdsalModule; +import org.opendaylight.infrautils.inject.guice.testutils.AnnotationsModule; +import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule; +import org.opendaylight.netconf.sal.restconf.api.RestConfConfig; +import org.opendaylight.netconf.sal.restconf.impl.Bierman02RestConfWiring; + +/** + * Tests if the {@link Bierman02RestConfWiring} works. + * + * @author Michael Vorburger.ch + */ +public class Bierman02RestConfWiringTest { + + public static class TestModule extends AbstractModule { + @Override + protected void configure() { + bind(Bierman02RestConfWiring.class).asEagerSingleton(); + bind(RestConfConfig.class).toInstance(() -> 9090); + bind(CustomFilterAdapterConfiguration.class).toInstance(listener -> { }); + } + } + + public @Rule GuiceRule guice = new GuiceRule(TestModule.class, + InMemoryMdsalModule.class, WebTestModule.class, AnnotationsModule.class); + + @Inject WebServer webServer; + @Inject TestWebClient webClient; + + @Test + public void testWiring() throws IOException, InterruptedException, URISyntaxException { + assertEquals(200, webClient.request("GET", "/restconf/modules/").statusCode()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java new file mode 100644 index 0000000..8443712 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2014, 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateBooleanFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateTrueFluentFuture; + +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.util.concurrent.FluentFuture; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.ReadFailedException; +import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction; +import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMNotificationService; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.api.DOMTransactionChain; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.PutResult; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.restconf.common.ErrorTags; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; + +/** + * Unit tests for BrokerFacade. + * + * @author Thomas Pantelis + */ +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class BrokerFacadeTest { + + @Mock + private DOMDataBroker domDataBroker; + @Mock + private DOMNotificationService domNotification; + @Mock + private DOMRpcService mockRpcService; + @Mock + private DOMMountPoint mockMountInstance; + @Mock + private DOMDataTreeReadTransaction readTransaction; + @Mock + private DOMDataTreeWriteTransaction writeTransaction; + @Mock + private DOMDataTreeReadWriteTransaction rwTransaction; + + private BrokerFacade brokerFacade; + private final NormalizedNode dummyNode = createDummyNode("test:module", "2014-01-09", "interfaces"); + private final FluentFuture<Optional<NormalizedNode>> dummyNodeInFuture = wrapDummyNode(dummyNode); + private final QName qname = TestUtils.buildQName("interfaces","test:module", "2014-01-09"); + private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(qname).build(); + private ControllerContext controllerContext; + + @Before + public void setUp() throws Exception { + controllerContext = TestRestconfUtils.newControllerContext( + TestUtils.loadSchemaContext("/full-versions/test-module", "/modules")); + + brokerFacade = BrokerFacade.newInstance(mockRpcService, domDataBroker, domNotification, controllerContext); + + when(domDataBroker.newReadOnlyTransaction()).thenReturn(readTransaction); + when(domDataBroker.newReadWriteTransaction()).thenReturn(rwTransaction); + when(domDataBroker.getExtensions()).thenReturn(ImmutableClassToInstanceMap.of( + DOMDataTreeChangeService.class, Mockito.mock(DOMDataTreeChangeService.class))); + } + + private static FluentFuture<Optional<NormalizedNode>> wrapDummyNode(final NormalizedNode dummyNode) { + return immediateFluentFuture(Optional.of(dummyNode)); + } + + private static FluentFuture<Boolean> wrapExistence(final boolean exists) { + return immediateBooleanFluentFuture(exists); + } + + /** + * Value of this node shouldn't be important for testing purposes. + */ + private static NormalizedNode createDummyNode(final String namespace, final String date, final String localName) { + return Builders.containerBuilder() + .withNodeIdentifier(new NodeIdentifier(QName.create(namespace, date, localName))) + .build(); + } + + @Test + public void testReadConfigurationData() { + when(readTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn( + dummyNodeInFuture); + + final NormalizedNode actualNode = brokerFacade.readConfigurationData(instanceID); + + assertSame("readConfigurationData", dummyNode, actualNode); + } + + @Test + public void testReadOperationalData() { + when(readTransaction.read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class))).thenReturn( + dummyNodeInFuture); + + final NormalizedNode actualNode = brokerFacade.readOperationalData(instanceID); + + assertSame("readOperationalData", dummyNode, actualNode); + } + + @Test + public void test503() throws Exception { + final RpcError error = RpcResultBuilder.newError(ErrorType.TRANSPORT, ErrorTag.RESOURCE_DENIED, + "Master is down. Please try again."); + doReturn(immediateFailedFluentFuture(new ReadFailedException("Read from transaction failed", error))) + .when(readTransaction).read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> brokerFacade.readConfigurationData(instanceID, "explicit")); + final List<RestconfError> errors = ex.getErrors(); + assertEquals(1, errors.size()); + assertEquals("getErrorTag", ErrorTags.RESOURCE_DENIED_TRANSPORT, errors.get(0).getErrorTag()); + assertEquals("getErrorType", ErrorType.TRANSPORT,errors.get(0).getErrorType()); + assertEquals("getErrorMessage", "Master is down. Please try again.", errors.get(0).getErrorMessage()); + } + + @Test + public void testInvokeRpc() throws Exception { + final DOMRpcResult expResult = mock(DOMRpcResult.class); + doReturn(immediateFluentFuture(expResult)).when(mockRpcService).invokeRpc(qname, dummyNode); + + final ListenableFuture<? extends DOMRpcResult> actualFuture = brokerFacade.invokeRpc(qname, + dummyNode); + assertNotNull("Future is null", actualFuture); + final DOMRpcResult actualResult = actualFuture.get(); + assertSame("invokeRpc", expResult, actualResult); + } + + @Test + public void testCommitConfigurationDataPut() throws Exception { + doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit(); + + doReturn(immediateFluentFuture(Optional.of(mock(NormalizedNode.class)))).when(rwTransaction) + .read(LogicalDatastoreType.CONFIGURATION, instanceID); + + final PutResult result = brokerFacade.commitConfigurationDataPut(mock(EffectiveModelContext.class), + instanceID, dummyNode, null, null); + + assertSame("commitConfigurationDataPut", CommitInfo.emptyFluentFuture(), result.getFutureOfPutData()); + + final InOrder inOrder = inOrder(domDataBroker, rwTransaction); + inOrder.verify(domDataBroker).newReadWriteTransaction(); + inOrder.verify(rwTransaction).put(LogicalDatastoreType.CONFIGURATION, instanceID, dummyNode); + inOrder.verify(rwTransaction).commit(); + } + + @Test + public void testCommitConfigurationDataPost() { + when(rwTransaction.exists(LogicalDatastoreType.CONFIGURATION, instanceID)) + .thenReturn(wrapExistence(false)); + + doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit(); + + final FluentFuture<? extends CommitInfo> actualFuture = brokerFacade + .commitConfigurationDataPost(mock(EffectiveModelContext.class), instanceID, dummyNode, null, + null); + + assertSame("commitConfigurationDataPost", CommitInfo.emptyFluentFuture(), actualFuture); + + final InOrder inOrder = inOrder(domDataBroker, rwTransaction); + inOrder.verify(domDataBroker).newReadWriteTransaction(); + inOrder.verify(rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, instanceID); + inOrder.verify(rwTransaction).put(LogicalDatastoreType.CONFIGURATION, instanceID, dummyNode); + inOrder.verify(rwTransaction).commit(); + } + + @Test(expected = RestconfDocumentedException.class) + public void testCommitConfigurationDataPostAlreadyExists() { + when(rwTransaction.exists(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))) + .thenReturn(immediateTrueFluentFuture()); + try { + // Schema context is only necessary for ensuring parent structure + brokerFacade.commitConfigurationDataPost((EffectiveModelContext) null, instanceID, dummyNode, + null, null); + } catch (final RestconfDocumentedException e) { + assertEquals("getErrorTag", ErrorTag.DATA_EXISTS, e.getErrors().get(0).getErrorTag()); + throw e; + } + } + + /** + * Positive test of delete operation when data to delete exits. Returned value and order of steps are validated. + */ + @Test + public void testCommitConfigurationDataDelete() throws Exception { + // assume that data to delete exists + prepareDataForDelete(true); + + // expected result + doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit(); + + // test + final FluentFuture<? extends CommitInfo> actualFuture = brokerFacade + .commitConfigurationDataDelete(instanceID); + + // verify result and interactions + assertSame("commitConfigurationDataDelete", CommitInfo.emptyFluentFuture(), actualFuture); + + // check exists, delete, submit + final InOrder inOrder = inOrder(domDataBroker, rwTransaction); + inOrder.verify(rwTransaction).exists(LogicalDatastoreType.CONFIGURATION, instanceID); + inOrder.verify(rwTransaction).delete(LogicalDatastoreType.CONFIGURATION, instanceID); + inOrder.verify(rwTransaction).commit(); + } + + /** + * Negative test of delete operation when data to delete does not exist. Error DATA_MISSING should be returned. + */ + @Test + public void testCommitConfigurationDataDeleteNoData() throws Exception { + // assume that data to delete does not exist + prepareDataForDelete(false); + + // try to delete and expect DATA_MISSING error + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> brokerFacade.commitConfigurationDataDelete(instanceID)); + final List<RestconfError> errors = ex.getErrors(); + assertEquals(1, errors.size()); + assertEquals(ErrorType.PROTOCOL, errors.get(0).getErrorType()); + assertEquals(ErrorTag.DATA_MISSING, errors.get(0).getErrorTag()); + } + + /** + * Prepare conditions to test delete operation. Data to delete exists or does not exist according to value of + * {@code assumeDataExists} parameter. + * @param assumeDataExists boolean to assume if data exists + */ + private void prepareDataForDelete(final boolean assumeDataExists) { + when(rwTransaction.exists(LogicalDatastoreType.CONFIGURATION, instanceID)) + .thenReturn(immediateBooleanFluentFuture(assumeDataExists)); + } + + @Test + public void testRegisterToListenDataChanges() { + final ListenerAdapter listener = Notificator.createListener(instanceID, "stream", + NotificationOutputType.XML, controllerContext); + + @SuppressWarnings("unchecked") + final ListenerRegistration<ListenerAdapter> mockRegistration = mock(ListenerRegistration.class); + + DOMDataTreeChangeService changeService = domDataBroker.getExtensions() + .getInstance(DOMDataTreeChangeService.class); + DOMDataTreeIdentifier loc = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, instanceID); + when(changeService.registerDataTreeChangeListener(eq(loc), eq(listener))).thenReturn(mockRegistration); + + brokerFacade.registerToListenDataChanges(LogicalDatastoreType.CONFIGURATION, Scope.BASE, listener); + + verify(changeService).registerDataTreeChangeListener(loc, listener); + + assertEquals("isListening", true, listener.isListening()); + + brokerFacade.registerToListenDataChanges(LogicalDatastoreType.CONFIGURATION, Scope.BASE, listener); + verifyNoMoreInteractions(changeService); + } + + /** + * Create, register, close and remove notification listener. + */ + @Test + public void testRegisterToListenNotificationChanges() throws Exception { + // create test notification listener + final String identifier = "create-notification-stream/toaster:toastDone"; + Notificator.createNotificationListener( + List.of(Absolute.of(QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toastDone"))), + identifier, "XML", controllerContext); + final NotificationListenerAdapter listener = Notificator.getNotificationListenerFor(identifier).get(0); + + // mock registration + final ListenerRegistration<NotificationListenerAdapter> registration = mock(ListenerRegistration.class); + when(domNotification.registerNotificationListener(listener, listener.getSchemaPath())) + .thenReturn(registration); + + // test to register listener for the first time + brokerFacade.registerToListenNotification(listener); + assertEquals("Registration was not successful", true, listener.isListening()); + + // try to register for the second time + brokerFacade.registerToListenNotification(listener); + assertEquals("Registration was not successful", true, listener.isListening()); + + // registrations should be invoked only once + verify(domNotification, times(1)).registerNotificationListener(listener, listener.getSchemaPath()); + + final DOMTransactionChain transactionChain = mock(DOMTransactionChain.class); + final DOMDataTreeWriteTransaction wTx = mock(DOMDataTreeWriteTransaction.class); + // close and remove test notification listener + listener.close(); + Notificator.removeListenerIfNoSubscriberExists(listener); + } + + /** + * Test Patch method on the server with no data. + */ + @Test + public void testPatchConfigurationDataWithinTransactionServer() throws Exception { + final PatchContext patchContext = mock(PatchContext.class); + + when(patchContext.getData()).thenReturn(List.of()); + // no mount point + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), null)) + .when(patchContext).getInstanceIdentifierContext(); + + doReturn(CommitInfo.emptyFluentFuture()).when(rwTransaction).commit(); + + final PatchStatusContext status = brokerFacade.patchConfigurationDataWithinTransaction(patchContext); + + // assert success + assertTrue("Patch operation should be successful on server", status.isOk()); + } + + /** + * Test Patch method on mounted device with no data. + */ + @Test + public void testPatchConfigurationDataWithinTransactionMount() throws Exception { + final PatchContext patchContext = mock(PatchContext.class); + final DOMMountPoint mountPoint = mock(DOMMountPoint.class); + final DOMDataBroker mountDataBroker = mock(DOMDataBroker.class); + final DOMDataTreeReadWriteTransaction transaction = mock(DOMDataTreeReadWriteTransaction.class); + + when(patchContext.getData()).thenReturn(List.of()); + // return mount point with broker + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint)) + .when(patchContext).getInstanceIdentifierContext(); + + when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.of(mountDataBroker)); + when(mountPoint.getService(DOMSchemaService.class)).thenReturn(Optional.empty()); + when(mountDataBroker.newReadWriteTransaction()).thenReturn(transaction); + doReturn(CommitInfo.emptyFluentFuture()).when(transaction).commit(); + + final PatchStatusContext status = brokerFacade.patchConfigurationDataWithinTransaction(patchContext); + + // assert success + assertTrue("Patch operation should be successful on mounted device", status.isOk()); + } + + /** + * Negative test for Patch operation when mounted device does not support {@link DOMDataBroker service.} + * Patch operation should fail with global error. + */ + @Test + public void testPatchConfigurationDataWithinTransactionMountFail() throws Exception { + final PatchContext patchContext = mock(PatchContext.class); + final DOMMountPoint mountPoint = mock(DOMMountPoint.class); + + doReturn(InstanceIdentifierContext.ofPath(SchemaInferenceStack.of(mock(EffectiveModelContext.class)), + mock(DataSchemaNode.class), YangInstanceIdentifier.empty(), mountPoint)) + .when(patchContext).getInstanceIdentifierContext(); + + // missing broker on mounted device + when(mountPoint.getService(DOMDataBroker.class)).thenReturn(Optional.empty()); + when(mountPoint.getService(DOMSchemaService.class)).thenReturn(Optional.empty()); + + final PatchStatusContext status = brokerFacade.patchConfigurationDataWithinTransaction(patchContext); + + // assert not successful operation with error + assertNotNull(status.getGlobalErrors()); + assertEquals(1, status.getGlobalErrors().size()); + assertEquals(ErrorType.APPLICATION, status.getGlobalErrors().get(0).getErrorType()); + assertEquals(ErrorTag.OPERATION_FAILED, status.getGlobalErrors().get(0).getErrorTag()); + + assertFalse("Patch operation should fail on mounted device without Broker", status.isOk()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug3595Test.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug3595Test.java new file mode 100644 index 0000000..0d7241d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug3595Test.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; + +public class Bug3595Test { + + private static final QName CONT_QNAME = QName.create("leafref:module", "2014-04-17", "cont"); + private static final QName LST_WITH_LFREF_KEY_QNAME = QName.create(CONT_QNAME, "lst-with-lfref-key"); + private static final QName LFREF_KEY_QNAME = QName.create(CONT_QNAME, "lfref-key"); + private static EffectiveModelContext schemaContext; + + private final ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + + @BeforeClass + public static void initialize() throws FileNotFoundException { + schemaContext = TestUtils.loadSchemaContext("/leafref/yang"); + Module module = TestUtils.findModule(schemaContext.getModules(), "leafref-module"); + assertNotNull(module); + module = TestUtils.findModule(schemaContext.getModules(), "referenced-module"); + assertNotNull(module); + } + + @Test + public void testLeafrefListKeyDeserializtion() { + final YangInstanceIdentifier node1IIexpected = YangInstanceIdentifier.of(CONT_QNAME) + .node(LST_WITH_LFREF_KEY_QNAME).node(NodeIdentifierWithPredicates.of( + LST_WITH_LFREF_KEY_QNAME, LFREF_KEY_QNAME, "node1")); + final InstanceIdentifierContext iiContext = + controllerContext.toInstanceIdentifier("leafref-module:cont/lst-with-lfref-key/node1"); + iiContext.getInstanceIdentifier(); + assertEquals(node1IIexpected, iiContext.getInstanceIdentifier()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug8072Test.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug8072Test.java new file mode 100644 index 0000000..a65babd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/Bug8072Test.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class Bug8072Test { + private static final String EXTERNAL_MODULE_NAME = "test-module"; + private static final QName MODULES_QNAME = QName.create("test:module", "2014-01-09", "modules"); + private static final QName MODULE_QNAME = QName.create("test:module", "2014-01-09", "module"); + private static final QName NAME_QNAME = QName.create("test:module", "2014-01-09", "name"); + private static final QName TYPE_QNAME = QName.create("test:module", "2014-01-09", "type"); + private static final QName MODULE_TYPE_QNAME = QName.create("test:module", "2014-01-09", "module-type"); + + private static EffectiveModelContext schemaContext; + + private final ControllerContext controllerContext; + + public Bug8072Test() throws FileNotFoundException { + final EffectiveModelContext mountPointContext = TestUtils.loadSchemaContext("/full-versions/test-module"); + final DOMMountPoint mountInstance = mock(DOMMountPoint.class); + controllerContext = TestRestconfUtils.newControllerContext(schemaContext, mountInstance); + doReturn(Optional.of(FixedDOMSchemaService.of(() -> mountPointContext))).when(mountInstance) + .getService(DOMSchemaService.class); + } + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs"); + assertEquals(0, schemaContext.findModules(EXTERNAL_MODULE_NAME).size()); + } + + @Test + public void testIdentityRefFromExternalModule() throws FileNotFoundException, ReactorException { + final InstanceIdentifierContext ctx = controllerContext.toInstanceIdentifier( + "simple-nodes:users/yang-ext:mount/test-module:modules/module/test-module:module-type/name"); + + final Map<QName, Object> keyValues = new HashMap<>(); + keyValues.put(NAME_QNAME, "name"); + keyValues.put(TYPE_QNAME, MODULE_TYPE_QNAME); + final YangInstanceIdentifier expectedYII = YangInstanceIdentifier.of(MODULES_QNAME).node(MODULE_QNAME) + .node(NodeIdentifierWithPredicates.of(MODULE_QNAME, keyValues)); + + assertEquals(expectedYII, ctx.getInstanceIdentifier()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CnSnToXmlAndJsonInstanceIdentifierTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CnSnToXmlAndJsonInstanceIdentifierTest.java new file mode 100644 index 0000000..96100cb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CnSnToXmlAndJsonInstanceIdentifierTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class CnSnToXmlAndJsonInstanceIdentifierTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + dataLoad("/instanceidentifier/yang", 4, "instance-identifier-module", "cont"); + } + + + private static void validateXmlOutput(final String xml) throws XMLStreamException { + final XMLInputFactory xmlInFactory = XMLInputFactory.newInstance(); + XMLEventReader eventReader; + + eventReader = xmlInFactory.createXMLEventReader(new ByteArrayInputStream(xml.getBytes())); + String augmentAugmentModulePrefix = null; + String augmentModulePrefix = null; + String instanceIdentifierModulePrefix = null; + while (eventReader.hasNext()) { + final XMLEvent nextEvent = eventReader.nextEvent(); + if (nextEvent.isStartElement()) { + final StartElement startElement = (StartElement) nextEvent; + if (startElement.getName().getLocalPart().equals("lf111")) { + final Iterator<?> prefixes = + startElement.getNamespaceContext().getPrefixes("augment:augment:module"); + + while (prefixes.hasNext() && augmentAugmentModulePrefix == null) { + final String prefix = (String) prefixes.next(); + if (!prefix.isEmpty()) { + augmentAugmentModulePrefix = prefix; + } + } + + augmentModulePrefix = startElement.getNamespaceContext().getPrefix("augment:module"); + instanceIdentifierModulePrefix = + startElement.getNamespaceContext().getPrefix("instance:identifier:module"); + break; + } + } + } + + assertNotNull(augmentAugmentModulePrefix); + assertNotNull(augmentModulePrefix); + assertNotNull(instanceIdentifierModulePrefix); + + final String instanceIdentifierValue = "/" + instanceIdentifierModulePrefix + ":cont/" + + instanceIdentifierModulePrefix + ":cont1/" + augmentModulePrefix + ":lst11[" + augmentModulePrefix + + ":keyvalue111='value1'][" + augmentModulePrefix + ":keyvalue112='value2']/" + + augmentAugmentModulePrefix + ":lf112"; + + assertTrue(xml.contains(instanceIdentifierValue)); + + } + + private static void validateXmlOutputWithLeafList(final String xml) throws XMLStreamException { + final XMLInputFactory xmlInFactory = XMLInputFactory.newInstance(); + XMLEventReader eventReader; + + eventReader = xmlInFactory.createXMLEventReader(new ByteArrayInputStream(xml.getBytes())); + String augmentModuleLfLstPrefix = null; + String iiModulePrefix = null; + while (eventReader.hasNext()) { + final XMLEvent nextEvent = eventReader.nextEvent(); + if (nextEvent.isStartElement()) { + final StartElement startElement = (StartElement) nextEvent; + if (startElement.getName().getLocalPart().equals("lf111")) { + final Iterator<?> prefixes = + startElement.getNamespaceContext().getPrefixes("augment:module:leaf:list"); + + while (prefixes.hasNext() && augmentModuleLfLstPrefix == null) { + final String prefix = (String) prefixes.next(); + if (!prefix.isEmpty()) { + augmentModuleLfLstPrefix = prefix; + } + } + iiModulePrefix = startElement.getNamespaceContext().getPrefix("instance:identifier:module"); + break; + } + } + } + + assertNotNull(augmentModuleLfLstPrefix); + assertNotNull(iiModulePrefix); + + final String instanceIdentifierValue = "/" + iiModulePrefix + ":cont/" + iiModulePrefix + ":cont1/" + + augmentModuleLfLstPrefix + ":lflst11[.='lflst11_1']"; + + assertTrue(xml.contains(instanceIdentifierValue)); + + } + + private static YangInstanceIdentifier createInstanceIdentifier() { + final List<PathArgument> pathArguments = new ArrayList<>(); + pathArguments.add(new NodeIdentifier(QName.create("instance:identifier:module", "cont"))); + pathArguments.add(new NodeIdentifier(QName.create("instance:identifier:module", "cont1"))); + + final QName qName = QName.create("augment:module", "lst11"); + final Map<QName, Object> keyValues = new HashMap<>(); + keyValues.put(QName.create("augment:module", "keyvalue111"), "value1"); + keyValues.put(QName.create("augment:module", "keyvalue112"), "value2"); + final NodeIdentifierWithPredicates nodeIdentifierWithPredicates = + NodeIdentifierWithPredicates.of(qName, keyValues); + pathArguments.add(nodeIdentifierWithPredicates); + + pathArguments.add(new NodeIdentifier(QName.create("augment:augment:module", "lf112"))); + + return YangInstanceIdentifier.create(pathArguments); + } + + private static YangInstanceIdentifier createInstanceIdentifierWithLeafList() { + final List<PathArgument> pathArguments = new ArrayList<>(); + pathArguments.add(new NodeIdentifier(QName.create("instance:identifier:module", "cont"))); + pathArguments.add(new NodeIdentifier(QName.create("instance:identifier:module", "cont1"))); + pathArguments.add(new NodeWithValue<>(QName.create("augment:module:leaf:list", "lflst11"), "lflst11_1")); + + return YangInstanceIdentifier.create(pathArguments); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java new file mode 100644 index 0000000..2110848 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CodecsExceptionsCatchingTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.FileNotFoundException; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class CodecsExceptionsCatchingTest extends JerseyTest { + + private RestconfImpl restConf; + private ControllerContext controllerContext; + + @Before + public void init() throws FileNotFoundException, ReactorException { + restConf = RestconfImpl.newInstance(mock(BrokerFacade.class), controllerContext); + controllerContext = TestRestconfUtils.newControllerContext(TestUtils.loadSchemaContext( + "/decoding-exception/yang")); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restConf, new NormalizedNodeJsonBodyWriter(), + new NormalizedNodeXmlBodyWriter(), new XmlNormalizedNodeBodyReader(controllerContext), + new JsonNormalizedNodeBodyReader(controllerContext)); + return resourceConfig; + } + + @Test + @Ignore // TODO RestconfDocumentedExceptionMapper needs be fixed before + public void stringToNumberConversionError() { + final Response response = target("/config/number:cont").request(MediaType.APPLICATION_XML).put( + Entity.entity("<cont xmlns=\"number\"><lf>3f</lf></cont>", MediaType.APPLICATION_XML)); + final String exceptionMessage = response.readEntity(String.class); + assertTrue(exceptionMessage.contains("invalid-value")); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java new file mode 100644 index 0000000..e379b3a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.Consumes; +import javax.ws.rs.Encoded; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.WriterParameters; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.QueryParametersParser; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CutDataToCorrectDepthTest extends JerseyTest { + + private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class); + + private static NormalizedNode depth1Cont; + private static NormalizedNode depth2Cont1; + private NormalizedNode globalPayload; + private static EffectiveModelContext schemaContextModules; + + private final ControllerContext controllerContext = + TestRestconfUtils.newControllerContext(schemaContextModules, null); + + @Path("/") + public class RestImpl { + + @GET + @Path("/config/{identifier:.+}") + @Produces({ "application/json", "application/xml" }) + public NormalizedNodeContext getData(@Encoded @PathParam("identifier") final String identifier, + @Context final UriInfo uriInfo) { + + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); + + NormalizedNode data = null; + if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) { + data = depth2Cont1; + } else if (identifier.equals("nested-module:depth1-cont")) { + data = depth1Cont; + } + + final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo); + return new NormalizedNodeContext(iiWithData, data, writerParameters); + } + + @GET + @Path("/operational/{identifier:.+}") + @Produces({ "application/json", "application/xml" }) + public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") final String identifier, + @Context final UriInfo uriInfo) { + return getData(identifier, uriInfo); + } + + @PUT + @Path("/config/{identifier:.+}") + @Consumes({ "application/json", "application/xml" }) + public void normalizedData(@Encoded @PathParam("identifier") final String identifier, + final NormalizedNodeContext payload) throws InterruptedException { + LOG.info("Payload: {}.", payload); + LOG.info("Instance identifier of payload: {}.", + payload.getInstanceIdentifierContext().getInstanceIdentifier()); + LOG.info("Data of payload: {}.", payload.getData()); + globalPayload = payload.getData(); + } + + @PUT + @Path("/operational/{identifier:.+}") + @Consumes({ "application/json", "application/xml" }) + public void normalizedDataOperational(@Encoded @PathParam("identifier") final String identifier, + final NormalizedNodeContext payload) throws InterruptedException { + normalizedData(identifier, payload); + } + } + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + schemaContextModules = TestUtils.loadSchemaContext("/modules"); + final Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module"); + assertNotNull(module); + + final UnkeyedListNode listAsUnkeyedList = unkeyedList( + "depth2-cont1", + unkeyedEntry("depth2-cont1", + container("depth3-cont1", + container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")), + leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value"))); + + final MapNode listAsMap = mapNode( + "depth2-list2", + mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"), + leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))); + + depth1Cont = container( + "depth1-cont", + listAsUnkeyedList, + listAsMap, + leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"), + container( + "depth2-cont2", + container("depth3-cont2", + container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")), + leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")), + leaf("depth2-leaf1", "depth2-leaf1-value")); + + depth2Cont1 = listAsUnkeyedList; + } + + // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized + // properly in readers + //@Test + public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException { + getDataWithUriDepthParameter("application/json"); + getDataWithUriDepthParameter("application/xml"); + } + + public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException { + Response response; + + // Test config with depth 1 + response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType) + .get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth1()); + + // Test config with depth 2 + response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType) + .get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth2()); + + // Test config with depth 3 + response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType) + .get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth3()); + + // Test config with depth 4 + response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType) + .get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth4()); + + // Test config with depth 5 + response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType) + .get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth5()); + + // Test config with depth unbounded + + response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded") + .request(mediaType).get(); + txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont"); + verifyResponse(nodeDataDepth5()); + } + + private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) { + final String responseStr = response.readEntity(String.class); + LOG.info("Response entity message: {}.", responseStr); + target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType)); + } + + private void verifyResponse(final NormalizedNode nodeData) throws WebApplicationException, IOException { + assertNotNull(globalPayload); + assertEquals(globalPayload, nodeData); + globalPayload = null; + } + + @Override + protected Application configure() { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(new RestImpl()); + resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class, + JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class, + RestconfDocumentedExceptionMapper.class); + return resourceConfig; + } + + private static LeafNode<?> leaf(final String localName, final Object value) { + return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build(); + } + + private static ContainerNode container(final String localName, final DataContainerChild... children) { + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder = + Builders.containerBuilder(); + for (final DataContainerChild child : children) { + containerBuilder.withChild(child); + } + containerBuilder.withNodeIdentifier(toIdentifier(localName)); + return containerBuilder.build(); + } + + private static UnkeyedListNode unkeyedList( + final String localName, + final UnkeyedListEntryNode... entryNodes) { + final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder(); + final NodeIdentifier identifier = toIdentifier(localName); + builder.withNodeIdentifier(identifier); + for (final UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) { + builder.withChild(unkeyedListEntryNode); + } + return builder.build(); + } + + private static UnkeyedListEntryNode unkeyedEntry(final String localName, final DataContainerChild... children) { + final DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = + Builders.unkeyedListEntryBuilder(); + builder.withNodeIdentifier(toIdentifier(localName)); + for (final DataContainerChild child : children) { + builder.withChild(child); + } + return builder.build(); + } + + private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) { + final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder = Builders.mapBuilder(); + builder.withNodeIdentifier(toIdentifier(localName)); + for (final MapEntryNode mapEntryNode : entryNodes) { + builder.withChild(mapEntryNode); + } + return builder.build(); + } + + private static MapEntryNode mapEntryNode(final String localName, final int keysNumber, + final DataContainerChild... children) { + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = + Builders.mapEntryBuilder(); + final Map<QName, Object> keys = new HashMap<>(); + for (int i = 0; i < keysNumber; i++) { + keys.put(children[i].getIdentifier().getNodeType(), children[i].body()); + } + builder.withNodeIdentifier(toIdentifier(localName, keys)); + + for (final DataContainerChild child : children) { + builder.withChild(child); + } + return builder.build(); + } + + private static LeafSetNode<?> leafList(final String localName, final String... children) { + final ListNodeBuilder<Object, SystemLeafSetNode<Object>> builder = Builders.leafSetBuilder(); + builder.withNodeIdentifier(toIdentifier(localName)); + for (final String child : children) { + builder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(toIdentifier(localName, child)) + .withValue(child).build()); + } + return builder.build(); + } + + private static NodeIdentifier toIdentifier(final String localName) { + return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-03", localName)); + } + + private static NodeIdentifierWithPredicates toIdentifier(final String localName, final Map<QName, Object> keys) { + return NodeIdentifierWithPredicates.of(QName.create("urn:nested:module", "2014-06-03", localName), keys); + } + + private static NodeWithValue<?> toIdentifier(final String localName, final Object value) { + return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-03", localName), value); + } + + private static UnkeyedListEntryNode nodeDataDepth3Operational() { + return unkeyedEntry("depth2-cont1", + container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")), + leaf("depth3-leaf1", "depth3-leaf1-value")); + } + + private static ContainerNode nodeDataDepth5() { + return container( + "depth1-cont", + unkeyedList( + "depth2-cont1", + unkeyedEntry("depth2-cont1", + container("depth3-cont1", + container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")), + leaf("depth4-leaf1", "depth4-leaf1-value")), + leaf("depth3-leaf1", "depth3-leaf1-value"))), + mapNode("depth2-list2", + mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"), + leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))), + leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"), + container( + "depth2-cont2", + container("depth3-cont2", + container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")), + leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")), + leaf("depth2-leaf1", "depth2-leaf1-value")); + } + + private static ContainerNode nodeDataDepth4() { + return container( + "depth1-cont", + unkeyedList("depth2-cont1", nodeDataDepth3Operational()), + mapNode("depth2-list2", + mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"), + leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))), + leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"), + container( + "depth2-cont2", + container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")), + leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value")); + } + + private static ContainerNode nodeDataDepth3() { + return container( + "depth1-cont", + unkeyedList("depth2-cont1", + unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))), + mapNode("depth2-list2", + mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"), + leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))), + leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"), + container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")), + leaf("depth2-leaf1", "depth2-leaf1-value")); + } + + private static ContainerNode nodeDataDepth2() { + return container( + "depth1-cont", + unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")), + mapNode("depth2-list2", + mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"), + leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"), +// leafList("depth2-lfLst1"), + leaf("depth2-leaf1", "depth2-leaf1-value")); + } + + private static ContainerNode nodeDataDepth1() { + return container("depth1-cont"); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyFuture.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyFuture.java new file mode 100644 index 0000000..30a0c50 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyFuture.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public class DummyFuture<T> implements Future<RpcResult<T>> { + + private final boolean cancel; + private final boolean isCancelled; + private final boolean isDone; + private final RpcResult<T> result; + + public DummyFuture() { + cancel = false; + isCancelled = false; + isDone = false; + result = null; + } + + private DummyFuture(final Builder<T> builder) { + cancel = builder.cancel; + isCancelled = builder.isCancelled; + isDone = builder.isDone; + result = builder.result; + } + + @Override + public boolean cancel(final boolean mayInterruptIfRunning) { + return cancel; + } + + @Override + public boolean isCancelled() { + return isCancelled; + } + + @Override + public boolean isDone() { + return isDone; + } + + @Override + public RpcResult<T> get() throws InterruptedException, ExecutionException { + return result; + } + + @Override + public RpcResult<T> get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, + TimeoutException { + return result; + } + + public static class Builder<T> { + + private boolean cancel; + private boolean isCancelled; + private boolean isDone; + private RpcResult<T> result; + + public Builder<T> cancel(final boolean newCancel) { + this.cancel = newCancel; + return this; + } + + public Builder<T> isCancelled(final boolean cancelled) { + this.isCancelled = cancelled; + return this; + } + + public Builder<T> isDone(final boolean done) { + this.isDone = done; + return this; + } + + public Builder<T> rpcResult(final RpcResult<T> newResult) { + this.result = newResult; + return this; + } + + public Future<RpcResult<T>> build() { + return new DummyFuture<>(this); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java new file mode 100644 index 0000000..ff01937 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.util.List; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public class DummyRpcResult<T> implements RpcResult<T> { + + private final boolean isSuccessful; + private final T result; + private final List<RpcError> errors; + + public DummyRpcResult() { + isSuccessful = false; + result = null; + errors = null; + } + + private DummyRpcResult(final Builder<T> builder) { + isSuccessful = builder.isSuccessful; + result = builder.result; + errors = builder.errors; + } + + @Override + public boolean isSuccessful() { + return isSuccessful; + } + + @Override + public T getResult() { + return result; + } + + @Override + public List<RpcError> getErrors() { + return errors; + } + + public static class Builder<T> { + private boolean isSuccessful; + private T result; + private List<RpcError> errors; + + public Builder<T> isSuccessful(final boolean successful) { + this.isSuccessful = successful; + return this; + } + + public Builder<T> result(final T newResult) { + this.result = newResult; + return this; + } + + public Builder<T> errors(final List<RpcError> newErrors) { + this.errors = newErrors; + return this; + } + + public RpcResult<T> build() { + return new DummyRpcResult<>(this); + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyType.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyType.java new file mode 100644 index 0000000..af408e6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyType.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.util.List; +import java.util.Optional; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.Status; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; + +public class DummyType implements TypeDefinition<DummyType> { + QName dummyQName = TestUtils.buildQName("dummy type", "simple:uri", "2012-12-17"); + + @Override + public QName getQName() { + return dummyQName; + } + + @Override + public Optional<String> getDescription() { + return Optional.empty(); + } + + @Override + public Optional<String> getReference() { + return Optional.empty(); + } + + @Override + public Status getStatus() { + // TODO Auto-generated method stub + return null; + } + + @Override + public List<UnknownSchemaNode> getUnknownSchemaNodes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public DummyType getBaseType() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Optional<String> getUnits() { + return Optional.empty(); + } + + @Override + public Optional<? extends Object> getDefaultValue() { + return Optional.empty(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java new file mode 100644 index 0000000..f4ed8f9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.Collection; +import java.util.Optional; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; + +public class ExpressionParserTest { + + private Collection<File> xmls; + + @Before + public void setup() throws Exception { + this.xmls = TestRestconfUtils.loadFiles("/notifications/xml/output/"); + } + + @Test + public void trueDownFilterTest() throws Exception { + final boolean parser = + parser("notification/data-changed-notification/data-change-event/data/toasterStatus='down'", + "data_change_notification_toaster_status_DOWN.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseDownFilterTest() throws Exception { + final boolean parser = + parser("notification/data-changed-notification/data-change-event/data/toasterStatus='up'", + "data_change_notification_toaster_status_DOWN.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberEqualsFilterTest() throws Exception { + final boolean parser = parser( + "notification/data-changed-notification/data-change-event/data/toasterStatus=1", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus=0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberLessFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<2", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberLessFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberLessEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=2", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberLessEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=-1", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberGreaterFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberGreaterFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>5", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + @Test + public void trueNumberGreaterEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=0", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertTrue(parser); + } + + @Test + public void falseNumberGreaterEqualsFilterTest() throws Exception { + final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=5", + "data_change_notification_toaster_status_NUMBER.xml"); + Assert.assertFalse(parser); + } + + private boolean parser(final String filter, final String fileName) throws Exception { + File xml = null; + for (final File file : this.xmls) { + if (file.getName().equals(fileName)) { + xml = file; + } + } + final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class); + final PathArgument pathValue = NodeIdentifier.create(QName.create("module", "2016-12-14", "localName")); + Mockito.when(path.getLastPathArgument()).thenReturn(pathValue); + final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON, + null); + listener.setQueryParams(Instant.now(), Optional.empty(), Optional.ofNullable(filter), false, false); + + // FIXME: do not use reflection here + final Class<?> superclass = listener.getClass().getSuperclass().getSuperclass(); + Method method = null; + for (final Method met : superclass.getDeclaredMethods()) { + if (met.getName().equals("parseFilterParam")) { + method = met; + } + } + if (method == null) { + throw new Exception("Methode parseFilterParam doesn't exist in " + superclass.getName()); + } + method.setAccessible(true); + return (boolean) method.invoke(listener, readFile(xml)); + } + + private static String readFile(final File xml) throws IOException { + try (BufferedReader br = new BufferedReader(new FileReader(xml, StandardCharsets.UTF_8))) { + final StringBuilder sb = new StringBuilder(); + String line = br.readLine(); + + while (line != null) { + sb.append(line); + sb.append("\n"); + line = br.readLine(); + } + return sb.toString(); + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java new file mode 100644 index 0000000..19386a1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/InvokeRpcMethodTest.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2013, 2015 Brocade Communication Systems, Inc., Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doCallRealMethod; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; + +import java.io.FileNotFoundException; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.ErrorTags; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class InvokeRpcMethodTest { + + private static UriInfo uriInfo; + private static EffectiveModelContext schemaContext; + + private final RestconfImpl restconfImpl; + private final ControllerContext controllerContext; + private final BrokerFacade brokerFacade = mock(BrokerFacade.class); + + public InvokeRpcMethodTest() { + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + } + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs", "/invoke-rpc"); + final Collection<? extends Module> allModules = schemaContext.getModules(); + assertNotNull(allModules); + final Module module = TestUtils.resolveModule("invoke-rpc-module", allModules); + assertNotNull(module); + + uriInfo = mock(UriInfo.class); + final MultivaluedMap<String, String> map = new MultivaluedHashMap<>(); + map.put("prettyPrint", List.of("true")); + doReturn(map).when(uriInfo).getQueryParameters(any(Boolean.class)); + } + + /** + * Test method invokeRpc in RestconfImpl class tests if composite node as input parameter of method invokeRpc + * (second argument) is wrapped to parent composite node which has QName equals to QName of rpc (resolved from + * string - first argument). + */ + @Test + public void invokeRpcMethodTest() { + controllerContext.findModuleNameByNamespace(XMLNamespace.of("invoke:rpc:module")); + + final NormalizedNodeContext payload = prepareDomPayload(); + + final NormalizedNodeContext rpcResponse = + restconfImpl.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo); + assertNotNull(rpcResponse); + assertNull(rpcResponse.getData()); + + } + + private NormalizedNodeContext prepareDomPayload() { + final EffectiveModelContext schema = controllerContext.getGlobalSchema(); + final Module rpcModule = schema.findModules("invoke-rpc-module").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "rpc-test"); + RpcDefinition rpcSchemaNode = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcSchemaNode = rpc; + break; + } + } + assertNotNull(rpcSchemaNode); + final InputSchemaNode rpcInputSchemaNode = rpcSchemaNode.getInput(); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> container = + SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode); + + final QName contQName = QName.create(rpcModule.getQNameModule(), "cont"); + final DataSchemaNode contSchemaNode = rpcInputSchemaNode.getDataChildByName(contQName); + assertTrue(contSchemaNode instanceof ContainerSchemaNode); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> contNode = + SchemaAwareBuilders.containerBuilder((ContainerSchemaNode) contSchemaNode); + + final QName lfQName = QName.create(rpcModule.getQNameModule(), "lf"); + final DataSchemaNode lfSchemaNode = ((ContainerSchemaNode) contSchemaNode).getDataChildByName(lfQName); + assertTrue(lfSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> lfNode = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) lfSchemaNode).withValue("any value").build(); + contNode.withChild(lfNode); + container.withChild(contNode.build()); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcSchemaNode, null), + container.build()); + } + + @Test + public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() { + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + + doReturn(immediateFailedFluentFuture(new DOMRpcImplementationNotAvailableException("testExeption"))) + .when(brokerFacade).invokeRpc(eq(qname), any()); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED, + Optional.empty(), Optional.empty()); + } + + void verifyRestconfDocumentedException(final RestconfDocumentedException restDocumentedException, final int index, + final ErrorType expErrorType, final ErrorTag expErrorTag, final Optional<String> expErrorMsg, + final Optional<String> expAppTag) { + + final List<RestconfError> errors = restDocumentedException.getErrors(); + assertTrue("RestconfError not found at index " + index, errors.size() > index); + + RestconfError actual = errors.get(index); + + assertEquals("getErrorType", expErrorType, actual.getErrorType()); + assertEquals("getErrorTag", expErrorTag, actual.getErrorTag()); + assertNotNull("getErrorMessage is null", actual.getErrorMessage()); + + if (expErrorMsg.isPresent()) { + assertEquals("getErrorMessage", expErrorMsg.get(), actual.getErrorMessage()); + } + + if (expAppTag.isPresent()) { + assertEquals("getErrorAppTag", expAppTag.get(), actual.getErrorAppTag()); + } + } + + @Test + public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() { + final List<RpcError> rpcErrors = List.of( + RpcResultBuilder.newError(ErrorType.TRANSPORT, new ErrorTag("bogusTag"), "foo"), + RpcResultBuilder.newWarning(ErrorType.RPC, ErrorTag.IN_USE, "bar", "app-tag", null, null)); + + final DOMRpcResult result = new DefaultDOMRpcResult(rpcErrors); + final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(path), any()); + + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + + // We are performing pass-through here of error-tag, hence the tag remains as specified, but we want to make + // sure the HTTP status remains the same as + final ErrorTag bogus = new ErrorTag("bogusTag"); + verifyRestconfDocumentedException(ex, 0, ErrorType.TRANSPORT, bogus, Optional.of("foo"), Optional.empty()); + assertEquals(ErrorTags.statusOf(ErrorTag.OPERATION_FAILED), ErrorTags.statusOf(bogus)); + + verifyRestconfDocumentedException(ex, 1, ErrorType.RPC, ErrorTag.IN_USE, Optional.of("bar"), + Optional.of("app-tag")); + } + + @Test + public void testInvokeRpcWithNoPayload_Success() { + final NormalizedNode resultObj = null; + final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj); + + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any()); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo); + assertNotNull(output); + assertEquals(null, output.getData()); + // additional validation in the fact that the restconfImpl does not + // throw an exception. + } + + @Test + public void testInvokeRpcWithEmptyOutput() { + final ContainerNode resultObj = mock(ContainerNode.class); + doReturn(Set.of()).when(resultObj).body(); + doCallRealMethod().when(resultObj).isEmpty(); + final DOMRpcResult expResult = new DefaultDOMRpcResult(resultObj); + + final QName qname = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)cancel-toast"); + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(qname), any()); + + WebApplicationException exceptionToBeThrown = assertThrows(WebApplicationException.class, + () -> restconfImpl.invokeRpc("toaster:cancel-toast", null, uriInfo)); + assertEquals(Response.Status.NO_CONTENT.getStatusCode(), exceptionToBeThrown.getResponse().getStatus()); + } + + @Test + public void testInvokeRpcMethodWithBadMethodName() { + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster:bad-method", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT, + Optional.empty(), Optional.empty()); + } + + @Test + @Ignore + public void testInvokeRpcMethodWithInput() { + final DOMRpcResult expResult = mock(DOMRpcResult.class); + final QName path = QName.create("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)make-toast"); + + final Module rpcModule = schemaContext.findModules("toaster").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "make-toast"); + + RpcDefinition rpcDef = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcDef = rpc; + break; + } + } + + assertNotNull(rpcDef); + + final NormalizedNodeContext payload = new NormalizedNodeContext( + InstanceIdentifierContext.ofLocalRpcInput(schemaContext, rpcDef), + SchemaAwareBuilders.containerBuilder(rpcDef.getInput()).build()); + + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(path), any(NormalizedNode.class)); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:make-toast", payload, uriInfo); + assertNotNull(output); + assertEquals(null, output.getData()); + // additional validation in the fact that the restconfImpl does not + // throw an exception. + } + + @Test + public void testThrowExceptionWhenSlashInModuleName() { + final RestconfDocumentedException ex = assertThrows(RestconfDocumentedException.class, + () -> restconfImpl.invokeRpc("toaster/slash", null, uriInfo)); + verifyRestconfDocumentedException(ex, 0, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + Optional.empty(), Optional.empty()); + } + + @Test + public void testInvokeRpcWithNoPayloadWithOutput_Success() { + final SchemaContext schema = controllerContext.getGlobalSchema(); + final Module rpcModule = schema.findModules("toaster").iterator().next(); + assertNotNull(rpcModule); + final QName rpcQName = QName.create(rpcModule.getQNameModule(), "testOutput"); + final QName rpcOutputQName = QName.create(rpcModule.getQNameModule(),"output"); + + RpcDefinition rpcDef = null; + ContainerLike rpcOutputSchemaNode = null; + for (final RpcDefinition rpc : rpcModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcOutputSchemaNode = rpc.getOutput(); + rpcDef = rpc; + break; + } + } + assertNotNull(rpcDef); + assertNotNull(rpcOutputSchemaNode); + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder = + SchemaAwareBuilders.containerBuilder(rpcOutputSchemaNode); + final DataSchemaNode leafSchema = rpcOutputSchemaNode + .getDataChildByName(QName.create(rpcModule.getQNameModule(), "textOut")); + assertTrue(leafSchema instanceof LeafSchemaNode); + final NormalizedNodeBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) leafSchema); + leafBuilder.withValue("brm"); + containerBuilder.withChild(leafBuilder.build()); + final ContainerNode container = containerBuilder.build(); + + final DOMRpcResult result = new DefaultDOMRpcResult(container); + + doReturn(immediateFluentFuture(result)).when(brokerFacade).invokeRpc(eq(rpcDef.getQName()), any()); + + final NormalizedNodeContext output = restconfImpl.invokeRpc("toaster:testOutput", null, uriInfo); + assertNotNull(output); + assertNotNull(output.getData()); + assertSame(container, output.getData()); + assertNotNull(output.getInstanceIdentifierContext()); + assertNotNull(output.getInstanceIdentifierContext().getSchemaContext()); + } + + /** + * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context. + * This rpc is then executed. + * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is + * invoked. + */ + @Test + public void testMountedRpcCallNoPayload_Success() throws Exception { + // FIXME find how to use mockito for it + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/JSONRestconfServiceImplTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/JSONRestconfServiceImplTest.java new file mode 100644 index 0000000..48db5de --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/JSONRestconfServiceImplTest.java @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; + +import com.google.common.io.Resources; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import javax.ws.rs.core.Response.Status; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMRpcException; +import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.JSONRestconfServiceImpl; +import org.opendaylight.netconf.sal.restconf.impl.PutResult; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.patch.PatchContext; +import org.opendaylight.restconf.common.patch.PatchStatusContext; +import org.opendaylight.restconf.common.patch.PatchStatusEntity; +import org.opendaylight.yangtools.yang.common.OperationFailedException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Uint32; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +/** + * Unit tests for JSONRestconfServiceImpl. + * + * @author Thomas Pantelis + */ +@Deprecated +public class JSONRestconfServiceImplTest { + static final String IETF_INTERFACES_NS = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + static final String IETF_INTERFACES_VERSION = "2013-07-04"; + static final QName INTERFACES_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "interfaces"); + static final QName INTERFACE_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "interface"); + static final QName NAME_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "name"); + static final QName TYPE_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "type"); + static final QName ENABLED_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "enabled"); + static final QName DESC_QNAME = QName.create(IETF_INTERFACES_NS, IETF_INTERFACES_VERSION, "description"); + + static final String TEST_MODULE_NS = "test:module"; + static final String TEST_MODULE_VERSION = "2014-01-09"; + static final QName TEST_CONT_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "cont"); + static final QName TEST_CONT1_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "cont1"); + static final QName TEST_LF11_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "lf11"); + static final QName TEST_LF12_QNAME = QName.create(TEST_MODULE_NS, TEST_MODULE_VERSION, "lf12"); + + static final String TOASTER_MODULE_NS = "http://netconfcentral.org/ns/toaster"; + static final String TOASTER_MODULE_VERSION = "2009-11-20"; + static final QName TOASTER_DONENESS_QNAME = + QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "toasterDoneness"); + static final QName TOASTER_TYPE_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "toasterToastType"); + static final QName WHEAT_BREAD_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "wheat-bread"); + static final QName MAKE_TOAST_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "make-toast"); + static final QName CANCEL_TOAST_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "cancel-toast"); + static final QName TEST_OUTPUT_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "testOutput"); + static final QName TEXT_OUT_QNAME = QName.create(TOASTER_MODULE_NS, TOASTER_MODULE_VERSION, "textOut"); + + private static EffectiveModelContext schemaContext; + + private final BrokerFacade brokerFacade = mock(BrokerFacade.class); + private final DOMMountPoint mockMountPoint = mock(DOMMountPoint.class); + private JSONRestconfServiceImpl service; + + @BeforeClass + public static void init() throws IOException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs"); + } + + @Before + public void setup() throws FileNotFoundException { + final EffectiveModelContext mountPointSchemaContext = TestUtils.loadSchemaContext("/full-versions/test-module"); + final ControllerContext controllerContext = + TestRestconfUtils.newControllerContext(schemaContext, mockMountPoint); + doReturn(java.util.Optional.of(FixedDOMSchemaService.of(() -> mountPointSchemaContext))).when(mockMountPoint) + .getService(eq(DOMSchemaService.class)); + + service = new JSONRestconfServiceImpl(controllerContext, + RestconfImpl.newInstance(brokerFacade, controllerContext)); + } + + private static String loadData(final String path) throws IOException { + return Resources.asCharSource(JSONRestconfServiceImplTest.class.getResource(path), + StandardCharsets.UTF_8).read(); + } + + @Test + public void testPut() throws Exception { + final PutResult result = mock(PutResult.class); + when(brokerFacade.commitConfigurationDataPut(any(EffectiveModelContext.class), + any(YangInstanceIdentifier.class), any(NormalizedNode.class), isNull(), isNull())) + .thenReturn(result); + doReturn(CommitInfo.emptyFluentFuture()).when(result).getFutureOfPutData(); + when(result.getStatus()).thenReturn(Status.OK); + final String uriPath = "ietf-interfaces:interfaces/interface/eth0"; + final String payload = loadData("/parts/ietf-interfaces_interfaces.json"); + service.put(uriPath, payload); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class); + verify(brokerFacade).commitConfigurationDataPut(any(EffectiveModelContext.class), capturedPath.capture(), + capturedNode.capture(), isNull(), isNull()); + + verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME, + new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"}); + + assertTrue("Expected MapEntryNode. Actual " + capturedNode.getValue().getClass(), + capturedNode.getValue() instanceof MapEntryNode); + final MapEntryNode actualNode = (MapEntryNode) capturedNode.getValue(); + assertEquals("MapEntryNode node type", INTERFACE_QNAME, actualNode.getIdentifier().getNodeType()); + verifyLeafNode(actualNode, NAME_QNAME, "eth0"); + verifyLeafNode(actualNode, TYPE_QNAME, "ethernetCsmacd"); + verifyLeafNode(actualNode, ENABLED_QNAME, Boolean.FALSE); + verifyLeafNode(actualNode, DESC_QNAME, "some interface"); + } + + @Test + public void testPutBehindMountPoint() throws Exception { + final PutResult result = mock(PutResult.class); + when(brokerFacade.commitMountPointDataPut(any(DOMMountPoint.class), + any(YangInstanceIdentifier.class), any(NormalizedNode.class), isNull(), isNull())) + .thenReturn(result); + doReturn(CommitInfo.emptyFluentFuture()).when(result).getFutureOfPutData(); + when(result.getStatus()).thenReturn(Status.OK); + final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1"; + final String payload = loadData("/full-versions/testCont1Data.json"); + + service.put(uriPath, payload); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class); + verify(brokerFacade).commitMountPointDataPut(same(mockMountPoint), capturedPath.capture(), + capturedNode.capture(), isNull(), isNull()); + + verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME); + + assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode); + final ContainerNode actualNode = (ContainerNode) capturedNode.getValue(); + assertEquals("ContainerNode node type", TEST_CONT1_QNAME, actualNode.getIdentifier().getNodeType()); + verifyLeafNode(actualNode, TEST_LF11_QNAME, "lf11 data"); + verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data"); + } + + @Test(expected = OperationFailedException.class) + @SuppressWarnings("checkstyle:IllegalThrows") + public void testPutFailure() throws Throwable { + final PutResult result = mock(PutResult.class); + + doReturn(immediateFailedFluentFuture(new TransactionCommitFailedException("mock"))).when(result) + .getFutureOfPutData(); + when(result.getStatus()).thenReturn(Status.OK); + when(brokerFacade.commitConfigurationDataPut(any(EffectiveModelContext.class), + any(YangInstanceIdentifier.class), any(NormalizedNode.class), anyString(), + anyString())).thenReturn(result); + + final String uriPath = "ietf-interfaces:interfaces/interface/eth0"; + final String payload = loadData("/parts/ietf-interfaces_interfaces.json"); + + service.put(uriPath, payload); + } + + @Test + public void testPost() throws Exception { + doReturn(CommitInfo.emptyFluentFuture()).when(brokerFacade).commitConfigurationDataPost( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), + isNull(), isNull()); + + final String uriPath = null; + final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json"); + + service.post(uriPath, payload); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class); + verify(brokerFacade).commitConfigurationDataPost(any(EffectiveModelContext.class), capturedPath.capture(), + capturedNode.capture(), isNull(), isNull()); + + verifyPath(capturedPath.getValue(), INTERFACES_QNAME); + + assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode); + final ContainerNode actualNode = (ContainerNode) capturedNode.getValue(); + assertEquals("ContainerNode node type", INTERFACES_QNAME, actualNode.getIdentifier().getNodeType()); + + final java.util.Optional<DataContainerChild> mapChild = actualNode.findChildByArg( + new NodeIdentifier(INTERFACE_QNAME)); + assertEquals(INTERFACE_QNAME.toString() + " present", true, mapChild.isPresent()); + assertTrue("Expected MapNode. Actual " + mapChild.get().getClass(), mapChild.get() instanceof MapNode); + final MapNode mapNode = (MapNode)mapChild.get(); + + final NodeIdentifierWithPredicates entryNodeID = NodeIdentifierWithPredicates.of( + INTERFACE_QNAME, NAME_QNAME, "eth0"); + final java.util.Optional<MapEntryNode> entryChild = mapNode.findChildByArg(entryNodeID); + assertEquals(entryNodeID.toString() + " present", true, entryChild.isPresent()); + final MapEntryNode entryNode = entryChild.get(); + verifyLeafNode(entryNode, NAME_QNAME, "eth0"); + verifyLeafNode(entryNode, TYPE_QNAME, "ethernetCsmacd"); + verifyLeafNode(entryNode, ENABLED_QNAME, Boolean.FALSE); + verifyLeafNode(entryNode, DESC_QNAME, "some interface"); + } + + @Test + public void testPostBehindMountPoint() throws Exception { + doReturn(CommitInfo.emptyFluentFuture()).when(brokerFacade).commitConfigurationDataPost( + any(DOMMountPoint.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), + isNull(), isNull()); + + final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont"; + final String payload = loadData("/full-versions/testCont1Data.json"); + + service.post(uriPath, payload); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class); + verify(brokerFacade).commitConfigurationDataPost(same(mockMountPoint), capturedPath.capture(), + capturedNode.capture(), isNull(), isNull()); + + verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME); + + assertTrue("Expected ContainerNode", capturedNode.getValue() instanceof ContainerNode); + final ContainerNode actualNode = (ContainerNode) capturedNode.getValue(); + assertEquals("ContainerNode node type", TEST_CONT1_QNAME, actualNode.getIdentifier().getNodeType()); + verifyLeafNode(actualNode, TEST_LF11_QNAME, "lf11 data"); + verifyLeafNode(actualNode, TEST_LF12_QNAME, "lf12 data"); + } + + @Test(expected = TransactionCommitFailedException.class) + @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" }) + public void testPostFailure() throws Throwable { + doReturn(immediateFailedFluentFuture(new TransactionCommitFailedException("mock"))).when(brokerFacade) + .commitConfigurationDataPost(any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), + any(NormalizedNode.class), isNull(), isNull()); + + final String uriPath = null; + final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json"); + + try { + service.post(uriPath, payload); + } catch (final OperationFailedException e) { + assertNotNull(e.getCause()); + throw e.getCause(); + } + } + + @Test + public void testPatch() throws Exception { + final PatchStatusContext result = mock(PatchStatusContext.class); + when(brokerFacade.patchConfigurationDataWithinTransaction(any(PatchContext.class))) + .thenReturn(result); + + when(result.getEditCollection()).thenReturn(List.of(new PatchStatusEntity("edit1", true, null))); + when(result.getGlobalErrors()).thenReturn(List.of()); + when(result.getPatchId()).thenReturn("1"); + final String uriPath = "ietf-interfaces:interfaces/interface/eth0"; + final String payload = loadData("/parts/ietf-interfaces_interfaces_patch.json"); + final Optional<String> patchResult = service.patch(uriPath, payload); + + assertTrue(patchResult.get().contains("\"ok\":[null]")); + } + + @Test + public void testPatchBehindMountPoint() throws Exception { + final PatchStatusContext result = mock(PatchStatusContext.class); + when(brokerFacade.patchConfigurationDataWithinTransaction(any(PatchContext.class))).thenReturn(result); + + when(result.getEditCollection()).thenReturn(List.of(new PatchStatusEntity("edit1", true, null))); + when(result.getGlobalErrors()).thenReturn(List.of()); + when(result.getPatchId()).thenReturn("1"); + + final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1"; + final String payload = loadData("/full-versions/testCont1DataPatch.json"); + + final Optional<String> patchResult = service.patch(uriPath, payload); + + assertTrue(patchResult.get().contains("\"ok\":[null]")); + } + + @Test(expected = OperationFailedException.class) + @SuppressWarnings("checkstyle:IllegalThrows") + public void testPatchFailure() throws Throwable { + final PatchStatusContext result = mock(PatchStatusContext.class); + when(brokerFacade.patchConfigurationDataWithinTransaction(any(PatchContext.class))) + .thenThrow(new TransactionCommitFailedException("Transaction failed")); + + final String uriPath = "ietf-interfaces:interfaces/interface/eth0"; + final String payload = loadData("/parts/ietf-interfaces_interfaces_patch.json"); + + final Optional<String> patchResult = service.patch(uriPath, payload); + + assertTrue("Patch output is not null", patchResult.isPresent()); + String patch = patchResult.get(); + assertTrue(patch.contains("TransactionCommitFailedException")); + } + + @Test + public void testDelete() throws Exception { + doReturn(CommitInfo.emptyFluentFuture()).when(brokerFacade) + .commitConfigurationDataDelete(any(YangInstanceIdentifier.class)); + + final String uriPath = "ietf-interfaces:interfaces/interface/eth0"; + + service.delete(uriPath); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + verify(brokerFacade).commitConfigurationDataDelete(capturedPath.capture()); + + verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME, + new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"}); + } + + @Test(expected = OperationFailedException.class) + public void testDeleteFailure() throws Exception { + final String invalidUriPath = "ietf-interfaces:interfaces/invalid"; + + service.delete(invalidUriPath); + } + + @Test + public void testGetConfig() throws Exception { + testGet(LogicalDatastoreType.CONFIGURATION); + } + + @Test + public void testGetOperational() throws Exception { + testGet(LogicalDatastoreType.OPERATIONAL); + } + + @Test + public void testGetWithNoData() throws OperationFailedException { + doReturn(null).when(brokerFacade).readConfigurationData(any(YangInstanceIdentifier.class), anyString()); + final String uriPath = "ietf-interfaces:interfaces"; + service.get(uriPath, LogicalDatastoreType.CONFIGURATION); + } + + @Test(expected = OperationFailedException.class) + public void testGetFailure() throws Exception { + final String invalidUriPath = "/ietf-interfaces:interfaces/invalid"; + service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION); + } + + @Test + public void testInvokeRpcWithInput() throws Exception { + final DOMRpcResult expResult = new DefaultDOMRpcResult((NormalizedNode)null); + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(eq(MAKE_TOAST_QNAME), + any(NormalizedNode.class)); + + final String uriPath = "toaster:make-toast"; + final String input = loadData("/full-versions/make-toast-rpc-input.json"); + + final Optional<String> output = service.invokeRpc(uriPath, Optional.of(input)); + + assertEquals("Output present", false, output.isPresent()); + + final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class); + verify(brokerFacade).invokeRpc(eq(MAKE_TOAST_QNAME), capturedNode.capture()); + + assertTrue("Expected ContainerNode. Actual " + capturedNode.getValue().getClass(), + capturedNode.getValue() instanceof ContainerNode); + final ContainerNode actualNode = (ContainerNode) capturedNode.getValue(); + verifyLeafNode(actualNode, TOASTER_DONENESS_QNAME, Uint32.valueOf(10)); + verifyLeafNode(actualNode, TOASTER_TYPE_QNAME, WHEAT_BREAD_QNAME); + } + + @Test + public void testInvokeRpcWithNoInput() throws Exception { + final DOMRpcResult expResult = new DefaultDOMRpcResult((NormalizedNode)null); + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(any(QName.class), any()); + + final String uriPath = "toaster:cancel-toast"; + + final Optional<String> output = service.invokeRpc(uriPath, Optional.empty()); + + assertEquals("Output present", false, output.isPresent()); + + verify(brokerFacade).invokeRpc(eq(CANCEL_TOAST_QNAME), any()); + } + + @Test + public void testInvokeRpcWithOutput() throws Exception { + final NormalizedNode outputNode = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TEST_OUTPUT_QNAME)) + .withChild(ImmutableNodes.leafNode(TEXT_OUT_QNAME, "foo")).build(); + final DOMRpcResult expResult = new DefaultDOMRpcResult(outputNode); + doReturn(immediateFluentFuture(expResult)).when(brokerFacade).invokeRpc(any(QName.class), any()); + + final String uriPath = "toaster:testOutput"; + + final Optional<String> output = service.invokeRpc(uriPath, Optional.empty()); + + assertEquals("Output present", true, output.isPresent()); + assertNotNull("Returned null response", output.get()); + assertThat("Missing \"textOut\"", output.get(), containsString("\"textOut\":\"foo\"")); + + verify(brokerFacade).invokeRpc(eq(TEST_OUTPUT_QNAME), any()); + } + + @Test(expected = OperationFailedException.class) + public void testInvokeRpcFailure() throws Exception { + final DOMRpcException exception = new DOMRpcImplementationNotAvailableException("testExeption"); + doReturn(immediateFailedFluentFuture(exception)).when(brokerFacade).invokeRpc(any(QName.class), + any(NormalizedNode.class)); + + final String uriPath = "toaster:cancel-toast"; + + service.invokeRpc(uriPath, Optional.empty()); + } + + void testGet(final LogicalDatastoreType datastoreType) throws OperationFailedException { + final MapEntryNode entryNode = ImmutableNodes.mapEntryBuilder(INTERFACE_QNAME, NAME_QNAME, "eth0") + .withChild(ImmutableNodes.leafNode(NAME_QNAME, "eth0")) + .withChild(ImmutableNodes.leafNode(TYPE_QNAME, "ethernetCsmacd")) + .withChild(ImmutableNodes.leafNode(ENABLED_QNAME, Boolean.TRUE)) + .withChild(ImmutableNodes.leafNode(DESC_QNAME, "eth interface")) + .build(); + + if (datastoreType == LogicalDatastoreType.CONFIGURATION) { + doReturn(entryNode).when(brokerFacade).readConfigurationData(any(YangInstanceIdentifier.class), + isNull()); + } else { + doReturn(entryNode).when(brokerFacade).readOperationalData(any(YangInstanceIdentifier.class)); + } + + final String uriPath = "/ietf-interfaces:interfaces/interface/eth0"; + + final Optional<String> optionalResp = service.get(uriPath, datastoreType); + assertEquals("Response present", true, optionalResp.isPresent()); + final String jsonResp = optionalResp.get(); + + assertNotNull("Returned null response", jsonResp); + assertThat("Missing \"name\"", jsonResp, containsString("\"name\":\"eth0\"")); + assertThat("Missing \"type\"", jsonResp, containsString("\"type\":\"ethernetCsmacd\"")); + assertThat("Missing \"enabled\"", jsonResp, containsString("\"enabled\":true")); + assertThat("Missing \"description\"", jsonResp, containsString("\"description\":\"eth interface\"")); + + final ArgumentCaptor<YangInstanceIdentifier> capturedPath = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + if (datastoreType == LogicalDatastoreType.CONFIGURATION) { + verify(brokerFacade).readConfigurationData(capturedPath.capture(), isNull()); + } else { + verify(brokerFacade).readOperationalData(capturedPath.capture()); + } + + verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME, + new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"}); + } + + void verifyLeafNode(final DataContainerNode parent, final QName leafType, final Object leafValue) { + final java.util.Optional<DataContainerChild> leafChild = parent.findChildByArg(new NodeIdentifier(leafType)); + assertTrue(leafType.toString() + " present", leafChild.isPresent()); + assertEquals(leafType.toString() + " value", leafValue, leafChild.get().body()); + } + + void verifyPath(final YangInstanceIdentifier path, final Object... expArgs) { + final List<PathArgument> pathArgs = path.getPathArguments(); + assertEquals("Arg count for actual path " + path, expArgs.length, pathArgs.size()); + int index = 0; + for (final PathArgument actual: pathArgs) { + QName expNodeType; + if (expArgs[index] instanceof Object[]) { + final Object[] listEntry = (Object[]) expArgs[index]; + expNodeType = (QName) listEntry[0]; + + assertTrue(actual instanceof NodeIdentifierWithPredicates); + final NodeIdentifierWithPredicates nip = (NodeIdentifierWithPredicates)actual; + assertEquals(String.format("Path arg %d keyValues size", index + 1), 1, nip.size()); + final QName expKey = (QName) listEntry[1]; + assertEquals(String.format("Path arg %d keyValue for %s", index + 1, expKey), listEntry[2], + nip.getValue(expKey)); + } else { + expNodeType = (QName) expArgs[index]; + } + + assertEquals(String.format("Path arg %d node type", index + 1), expNodeType, actual.getNodeType()); + index++; + } + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java new file mode 100644 index 0000000..2bddec2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.opendaylight.controller.sal.restconf.impl.test.RestOperationUtils.JSON; +import static org.opendaylight.controller.sal.restconf.impl.test.RestOperationUtils.XML; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; + +public class MediaTypesTest extends JerseyTest { + + private static String jsonData; + private static String xmlData; + + private RestconfService restconfService; + + @BeforeClass + public static void init() throws IOException { + final String jsonPath = RestconfImplTest.class.getResource("/parts/ietf-interfaces_interfaces.json").getPath(); + jsonData = TestUtils.loadTextFile(jsonPath); + final InputStream xmlStream = + RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml"); + xmlData = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream)); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue());' + restconfService = mock(RestconfService.class); + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfService, new NormalizedNodeJsonBodyWriter(), + new NormalizedNodeXmlBodyWriter()); + return resourceConfig; + } + + @Test + @Ignore + public void testPostOperationsWithInputDataMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/operations/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + when(restconfService.invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class))) + .thenReturn(null); + post(uri, Draft02.MediaTypes.OPERATION + JSON, Draft02.MediaTypes.OPERATION + JSON, jsonData); + verify(restconfService, times(1)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, Draft02.MediaTypes.OPERATION + XML, Draft02.MediaTypes.OPERATION + XML, xmlData); + verify(restconfService, times(2)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON, jsonData); + verify(restconfService, times(3)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, MediaType.APPLICATION_XML, MediaType.APPLICATION_XML, xmlData); + verify(restconfService, times(4)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, MediaType.TEXT_XML, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(5)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(6)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + + // negative tests + post(uri, MediaType.TEXT_PLAIN, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(6)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, MediaType.TEXT_XML, MediaType.TEXT_PLAIN, xmlData); + verify(restconfService, times(6)).invokeRpc(eq(uriPath), any(NormalizedNodeContext.class), any(UriInfo.class)); + } + + @Test + public void testGetConfigMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/config/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + when(restconfService.readConfigurationData(eq(uriPath), any(UriInfo.class))).thenReturn(null); + get(uri, Draft02.MediaTypes.DATA + JSON); + verify(restconfService, times(1)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + get(uri, Draft02.MediaTypes.DATA + XML); + verify(restconfService, times(2)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.APPLICATION_JSON); + verify(restconfService, times(3)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.APPLICATION_XML); + verify(restconfService, times(4)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.TEXT_XML); + verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + + // negative tests + get(uri, MediaType.TEXT_PLAIN); + verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class)); + } + + @Test + public void testGetOperationalMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/operational/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + when(restconfService.readOperationalData(eq(uriPath), any(UriInfo.class))).thenReturn(null); + get(uri, Draft02.MediaTypes.DATA + JSON); + verify(restconfService, times(1)).readOperationalData(eq(uriPath), any(UriInfo.class)); + get(uri, Draft02.MediaTypes.DATA + XML); + verify(restconfService, times(2)).readOperationalData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.APPLICATION_JSON); + verify(restconfService, times(3)).readOperationalData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.APPLICATION_XML); + verify(restconfService, times(4)).readOperationalData(eq(uriPath), any(UriInfo.class)); + get(uri, MediaType.TEXT_XML); + verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class)); + + // negative tests + get(uri, MediaType.TEXT_PLAIN); + verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class)); + } + + @Test + @Ignore + public void testPutConfigMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/config/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + final UriInfo uriInfo = Mockito.mock(UriInfo.class); + when(restconfService.updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), uriInfo)) + .thenReturn(null); + put(uri, null, Draft02.MediaTypes.DATA + JSON, jsonData); + verify(restconfService, times(1)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + put(uri, null, Draft02.MediaTypes.DATA + XML, xmlData); + verify(restconfService, times(2)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + put(uri, null, MediaType.APPLICATION_JSON, jsonData); + verify(restconfService, times(3)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + put(uri, null, MediaType.APPLICATION_XML, xmlData); + verify(restconfService, times(4)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + put(uri, null, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(5)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + put(uri, "fooMediaType", MediaType.TEXT_XML, xmlData); + verify(restconfService, times(6)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + uriInfo); + } + + @Test + @Ignore + public void testPostConfigWithPathMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/config/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + when(restconfService.createConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), + any(UriInfo.class))).thenReturn(null); + post(uri, null, Draft02.MediaTypes.DATA + JSON, jsonData); + verify(restconfService, times(1)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, Draft02.MediaTypes.DATA + XML, xmlData); + verify(restconfService, times(2)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.APPLICATION_JSON, jsonData); + verify(restconfService, times(3)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.APPLICATION_XML, xmlData); + verify(restconfService, times(4)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(5)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, "fooMediaType", MediaType.TEXT_XML, xmlData); + verify(restconfService, times(6)).createConfigurationData(eq(uriPath), + any(NormalizedNodeContext.class), any(UriInfo.class)); + } + + @Test + @Ignore + public void testPostConfigMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/config/"; + final String uri = uriPrefix; + when(restconfService.createConfigurationData(any(NormalizedNodeContext.class), + any(UriInfo.class))).thenReturn(null); + post(uri, null, Draft02.MediaTypes.DATA + JSON, jsonData); + verify(restconfService, times(1)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, Draft02.MediaTypes.DATA + XML, xmlData); + verify(restconfService, times(2)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.APPLICATION_JSON, jsonData); + verify(restconfService, times(3)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.APPLICATION_XML, xmlData); + verify(restconfService, times(4)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, null, MediaType.TEXT_XML, xmlData); + verify(restconfService, times(5)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + post(uri, "fooMediaType", MediaType.TEXT_XML, xmlData); + verify(restconfService, times(6)).createConfigurationData( + any(NormalizedNodeContext.class), any(UriInfo.class)); + } + + @Test + public void testDeleteConfigMediaTypes() throws UnsupportedEncodingException { + final String uriPrefix = "/config/"; + final String uriPath = "ietf-interfaces:interfaces"; + final String uri = uriPrefix + uriPath; + when(restconfService.deleteConfigurationData(eq(uriPath))).thenReturn(null); + target(uri).request("fooMediaType").delete(); + verify(restconfService, times(1)).deleteConfigurationData(uriPath); + } + + private int get(final String uri, final String acceptMediaType) { + return target(uri).request(acceptMediaType).get().getStatus(); + } + + private int put(final String uri, final String acceptMediaType, final String contentTypeMediaType, + final String data) { + if (acceptMediaType == null) { + return target(uri).request().put(Entity.entity(data, contentTypeMediaType)).getStatus(); + } + return target(uri).request(acceptMediaType).put(Entity.entity(data, contentTypeMediaType)).getStatus(); + } + + private int post(final String uri, final String acceptMediaType, final String contentTypeMediaType, + final String data) { + if (acceptMediaType == null) { + if (contentTypeMediaType == null || data == null) { + return target(uri).request().post(null).getStatus(); + } + return target(uri).request().post(Entity.entity(data, contentTypeMediaType)).getStatus(); + } + if (contentTypeMediaType == null || data == null) { + return target(uri).request(acceptMediaType).post(null).getStatus(); + } + return target(uri).request(acceptMediaType).post(Entity.entity(data, contentTypeMediaType)).getStatus(); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/NormalizeNodeTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/NormalizeNodeTest.java new file mode 100644 index 0000000..3af5f96 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/NormalizeNodeTest.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class NormalizeNodeTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialization() throws FileNotFoundException, ReactorException { + dataLoad("/normalize-node/yang/"); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestCodecExceptionsTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestCodecExceptionsTest.java new file mode 100644 index 0000000..bd2d48b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestCodecExceptionsTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; + +import org.junit.Test; +import org.opendaylight.netconf.sal.restconf.impl.RestCodec; +import org.opendaylight.yangtools.concepts.IllegalArgumentCodec; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.ri.type.BaseTypes; + +public class RestCodecExceptionsTest { + @Test + public void serializeExceptionTest() { + final IllegalArgumentCodec<Object, Object> codec = RestCodec.from( + BaseTypes.bitsTypeBuilder(QName.create("test", "2014-05-30", "test")).build(), null, null); + final String serializedValue = (String) codec.serialize("incorrect value"); // set + // expected + assertEquals("incorrect value", serializedValue); + } + + @Test + public void deserializeExceptionTest() { + final IdentityrefTypeDefinition mockedIidentityrefType = mock(IdentityrefTypeDefinition.class); + + final IllegalArgumentCodec<Object, Object> codec = RestCodec.from(mockedIidentityrefType, null, null); + assertNull(codec.deserialize("incorrect value")); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestDeleteOperationTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestDeleteOperationTest.java new file mode 100644 index 0000000..3ebbf28 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestDeleteOperationTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture; + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class RestDeleteOperationTest extends JerseyTest { + private static EffectiveModelContext schemaContext; + + private ControllerContext controllerContext; + private BrokerFacade brokerFacade; + private RestconfImpl restconfImpl; + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/test-config-data/yang1"); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + controllerContext.setSchemas(schemaContext); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfImpl, new NormalizedNodeJsonBodyWriter(), + new NormalizedNodeXmlBodyWriter(), new XmlNormalizedNodeBodyReader(controllerContext), + new JsonNormalizedNodeBodyReader(controllerContext), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + @Test + public void deleteConfigStatusCodes() throws UnsupportedEncodingException { + final String uri = "/config/test-interface:interfaces"; + doReturn(CommitInfo.emptyFluentFuture()).when(brokerFacade) + .commitConfigurationDataDelete(any(YangInstanceIdentifier.class)); + Response response = target(uri).request(MediaType.APPLICATION_XML).delete(); + assertEquals(200, response.getStatus()); + + doThrow(RestconfDocumentedException.class).when(brokerFacade).commitConfigurationDataDelete( + any(YangInstanceIdentifier.class)); + response = target(uri).request(MediaType.APPLICATION_XML).delete(); + assertEquals(500, response.getStatus()); + } + + @Test + public void deleteFailTest() throws Exception { + final String uri = "/config/test-interface:interfaces"; + doReturn(immediateFailedFluentFuture(new TransactionCommitFailedException("failed test"))).when(brokerFacade) + .commitConfigurationDataDelete(any(YangInstanceIdentifier.class)); + final Response response = target(uri).request(MediaType.APPLICATION_XML).delete(); + assertEquals(500, response.getStatus()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java new file mode 100644 index 0000000..d0b30cb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetAugmentedElementWhenEqualNamesTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class RestGetAugmentedElementWhenEqualNamesTest { + + private static EffectiveModelContext schemaContext; + + private final ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + + @BeforeClass + public static void init() throws FileNotFoundException { + schemaContext = TestUtils.loadSchemaContext("/common/augment/yang"); + } + + @Test + public void augmentedNodesInUri() { + InstanceIdentifierContext iiWithData = + controllerContext.toInstanceIdentifier("main:cont/augment-main-a:cont1"); + assertEquals(XMLNamespace.of("ns:augment:main:a"), iiWithData.getSchemaNode().getQName().getNamespace()); + iiWithData = controllerContext.toInstanceIdentifier("main:cont/augment-main-b:cont1"); + assertEquals(XMLNamespace.of("ns:augment:main:b"), iiWithData.getSchemaNode().getQName().getNamespace()); + } + + @Test + public void nodeWithoutNamespaceHasMoreAugments() { + final var ex = assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("main:cont/cont1")); + assertThat(ex.getErrors().get(0).getErrorMessage(), + containsString("is added as augment from more than one module")); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java new file mode 100644 index 0000000..65fa8e0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +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 static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class RestGetOperationTest extends JerseyTest { + + static class NodeData { + Object key; + Object data; // List for a CompositeNode, value Object for a SimpleNode + + NodeData(final Object key, final Object data) { + this.key = key; + this.data = data; + } + } + + private static EffectiveModelContext schemaContextYangsIetf; + private static EffectiveModelContext schemaContextTestModule; + private static EffectiveModelContext schemaContextModules; + private static EffectiveModelContext schemaContextBehindMountPoint; + + private static NormalizedNode answerFromGet; + + private BrokerFacade brokerFacade; + private RestconfImpl restconfImpl; + private ControllerContext controllerContext; + private DOMMountPoint mountInstance; + + private static final String RESTCONF_NS = "urn:ietf:params:xml:ns:yang:ietf-restconf"; + + @BeforeClass + public static void init() throws Exception { + schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs"); + schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module"); + schemaContextModules = TestUtils.loadSchemaContext("/modules"); + schemaContextBehindMountPoint = TestUtils.loadSchemaContext("/modules/modules-behind-mount-point"); + + answerFromGet = TestUtils.prepareNormalizedNodeWithIetfInterfacesInterfacesData(); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + + mountInstance = mock(DOMMountPoint.class); + controllerContext = TestRestconfUtils.newControllerContext(schemaContextYangsIetf, mountInstance); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfImpl, new NormalizedNodeJsonBodyWriter(), + new NormalizedNodeXmlBodyWriter(), new XmlNormalizedNodeBodyReader(controllerContext), + new JsonNormalizedNodeBodyReader(controllerContext), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + private void setControllerContext(final EffectiveModelContext schemaContext) { + controllerContext.setSchemas(schemaContext); + } + + /** + * Tests of status codes for "/operational/{identifier}". + */ + @Test + public void getOperationalStatusCodes() throws Exception { + setControllerContext(schemaContextYangsIetf); + mockReadOperationalDataMethod(); + String uri = "/operational/ietf-interfaces:interfaces/interface/eth0"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + + uri = "/operational/wrong-module:interfaces/interface/eth0"; + assertEquals(400, get(uri, MediaType.APPLICATION_XML)); + } + + /** + * Tests of status codes for "/config/{identifier}". + */ + @Test + public void getConfigStatusCodes() throws Exception { + setControllerContext(schemaContextYangsIetf); + mockReadConfigurationDataMethod(); + String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + + uri = "/config/wrong-module:interfaces/interface/eth0"; + assertEquals(400, get(uri, MediaType.APPLICATION_XML)); + } + + /** + * MountPoint test. URI represents mount point. + */ + @Test + public void getDataWithUrlMountPoint() throws Exception { + when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class), + isNull())).thenReturn(prepareCnDataForMountPointTest(false)); + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextTestModule))); + + String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/cont1"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + + uri = "/config/ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + } + + /** + * MountPoint test. URI represents mount point. + * Slashes in URI behind mount point. lst1 element with key GigabitEthernet0%2F0%2F0%2F0 (GigabitEthernet0/0/0/0) is + * requested via GET HTTP operation. It is tested whether %2F character is replaced with simple / in + * InstanceIdentifier parameter in method + * {@link BrokerFacade#readConfigurationData(DOMMountPoint, YangInstanceIdentifier)} which is called in + * method {@link RestconfImpl#readConfigurationData} + */ + @Test + public void getDataWithSlashesBehindMountPoint() throws Exception { + final YangInstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList(); + when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier), + isNull())).thenReturn(prepareCnDataForSlashesBehindMountPointTest()); + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextTestModule))); + + final String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/" + + "test-module:cont/lst1/GigabitEthernet0%2F0%2F0%2F0"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + } + + private static YangInstanceIdentifier prepareInstanceIdentifierForList() throws Exception { + final List<PathArgument> parameters = new ArrayList<>(); + + final QName qNameCont = newTestModuleQName("cont"); + final QName qNameList = newTestModuleQName("lst1"); + final QName qNameKeyList = newTestModuleQName("lf11"); + + parameters.add(new NodeIdentifier(qNameCont)); + parameters.add(new NodeIdentifier(qNameList)); + parameters.add(NodeIdentifierWithPredicates.of(qNameList, qNameKeyList, "GigabitEthernet0/0/0/0")); + return YangInstanceIdentifier.create(parameters); + } + + private static QName newTestModuleQName(final String localPart) throws Exception { + return QName.create("test:module", "2014-01-09", localPart); + } + + @Test + public void getDataMountPointIntoHighestElement() throws Exception { + when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class), + isNull())).thenReturn(prepareCnDataForMountPointTest(true)); + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextTestModule))); + + final String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + } + + @Test + public void getDataWithIdentityrefInURL() throws Exception { + setControllerContext(schemaContextTestModule); + + final QName moduleQN = newTestModuleQName("module"); + final ImmutableMap<QName, Object> keyMap = ImmutableMap.of( + newTestModuleQName("type"), newTestModuleQName("test-identity"), + newTestModuleQName("name"), "foo"); + final YangInstanceIdentifier iid = YangInstanceIdentifier.builder().node(newTestModuleQName("modules")) + .node(moduleQN).nodeWithKey(moduleQN, keyMap).build(); + final NormalizedNode data = ImmutableMapNodeBuilder.create() + .withNodeIdentifier(new NodeIdentifier(moduleQN)) + .withChild(ImmutableNodes.mapEntryBuilder() + .withNodeIdentifier(NodeIdentifierWithPredicates.of(moduleQN, keyMap)) + .withChild(ImmutableNodes.leafNode(newTestModuleQName("type"), newTestModuleQName("test-identity"))) + .withChild(ImmutableNodes.leafNode(newTestModuleQName("name"), "foo")) + .withChild(ImmutableNodes.leafNode(newTestModuleQName("data"), "bar")).build()).build(); + when(brokerFacade.readConfigurationData(iid, null)).thenReturn(data); + + final String uri = "/config/test-module:modules/module/test-module:test-identity/foo"; + assertEquals(200, get(uri, MediaType.APPLICATION_XML)); + } + + // /modules + @Test + public void getModulesTest() throws Exception { + setControllerContext(schemaContextModules); + + final String uri = "/modules"; + + Response response = target(uri).request("application/yang.api+json").get(); + validateModulesResponseJson(response); + + response = target(uri).request("application/yang.api+xml").get(); + validateModulesResponseXml(response,schemaContextModules); + } + + // /streams/ + @Test + @Ignore // FIXME : find why it is fail by in gerrit build + public void getStreamsTest() throws Exception { + setControllerContext(schemaContextModules); + + final String uri = "/streams"; + + Response response = target(uri).request("application/yang.api+json").get(); + final String responseBody = response.readEntity(String.class); + assertEquals(200, response.getStatus()); + assertNotNull(responseBody); + assertTrue(responseBody.contains("streams")); + + response = target(uri).request("application/yang.api+xml").get(); + assertEquals(200, response.getStatus()); + final Document responseXmlBody = response.readEntity(Document.class); + assertNotNull(responseXmlBody); + final Element rootNode = responseXmlBody.getDocumentElement(); + + assertEquals("streams", rootNode.getLocalName()); + assertEquals(RESTCONF_NS, rootNode.getNamespaceURI()); + } + + // /modules/module + @Test + public void getModuleTest() throws Exception { + setControllerContext(schemaContextModules); + + final String uri = "/modules/module/module2/2014-01-02"; + + Response response = target(uri).request("application/yang.api+xml").get(); + assertEquals(200, response.getStatus()); + final Document responseXml = response.readEntity(Document.class); + + final QName qname = assertedModuleXmlToModuleQName(responseXml.getDocumentElement()); + assertNotNull(qname); + + assertEquals("module2", qname.getLocalName()); + assertEquals("module:2", qname.getNamespace().toString()); + assertEquals("2014-01-02", qname.getRevision().get().toString()); + + response = target(uri).request("application/yang.api+json").get(); + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody) + .find()); + final String[] split = responseBody.split("\"module\""); + assertEquals("\"module\" element is returned more then once", 2, split.length); + + } + + // /operations + @Ignore + @Test + public void getOperationsTest() throws Exception { + setControllerContext(schemaContextModules); + + final String uri = "/operations"; + + Response response = target(uri).request("application/yang.api+xml").get(); + assertEquals(500, response.getStatus()); + final Document responseDoc = response.readEntity(Document.class); + validateOperationsResponseXml(responseDoc, schemaContextModules); + + response = target(uri).request("application/yang.api+json").get(); + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + assertTrue("Json response for /operations dummy-rpc1-module1 is incorrect", + validateOperationsResponseJson(responseBody, "dummy-rpc1-module1", "module1").find()); + assertTrue("Json response for /operations dummy-rpc2-module1 is incorrect", + validateOperationsResponseJson(responseBody, "dummy-rpc2-module1", "module1").find()); + assertTrue("Json response for /operations dummy-rpc1-module2 is incorrect", + validateOperationsResponseJson(responseBody, "dummy-rpc1-module2", "module2").find()); + assertTrue("Json response for /operations dummy-rpc2-module2 is incorrect", + validateOperationsResponseJson(responseBody, "dummy-rpc2-module2", "module2").find()); + } + + private static void validateOperationsResponseXml(final Document responseDoc, final SchemaContext schemaContext) { + + final Element operationsElem = responseDoc.getDocumentElement(); + assertEquals(RESTCONF_NS, operationsElem.getNamespaceURI()); + assertEquals("operations", operationsElem.getLocalName()); + + final NodeList operationsList = operationsElem.getChildNodes(); + final HashSet<String> foundOperations = new HashSet<>(); + + for (int i = 0; i < operationsList.getLength(); i++) { + final org.w3c.dom.Node operation = operationsList.item(i); + foundOperations.add(operation.getLocalName()); + } + + for (final RpcDefinition schemaOp : schemaContext.getOperations()) { + assertTrue(foundOperations.contains(schemaOp.getQName().getLocalName())); + } + } + + // /operations/pathToMountPoint/yang-ext:mount + @Ignore + @Test + public void getOperationsBehindMountPointTest() throws Exception { + setControllerContext(schemaContextModules); + + mockMountPoint(); + + final String uri = "/operations/ietf-interfaces:interfaces/interface/0/yang-ext:mount/"; + + Response response = target(uri).request("application/yang.api+xml").get(); + assertEquals(500, response.getStatus()); + + final Document responseDoc = response.readEntity(Document.class); + validateOperationsResponseXml(responseDoc, schemaContextBehindMountPoint); + + response = target(uri).request("application/yang.api+json").get(); + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + assertTrue("Json response for /operations/mount_point rpc-behind-module1 is incorrect", + validateOperationsResponseJson(responseBody, "rpc-behind-module1", "module1-behind-mount-point").find()); + assertTrue("Json response for /operations/mount_point rpc-behind-module2 is incorrect", + validateOperationsResponseJson(responseBody, "rpc-behind-module2", "module2-behind-mount-point").find()); + + } + + private static Matcher validateOperationsResponseJson(final String searchIn, final String rpcName, + final String moduleName) { + final StringBuilder regex = new StringBuilder(); + regex.append(".*\"" + rpcName + "\""); + final Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL); + return ptrn.matcher(searchIn); + + } + + // /restconf/modules/pathToMountPoint/yang-ext:mount + @Test + public void getModulesBehindMountPoint() throws Exception { + setControllerContext(schemaContextModules); + + mockMountPoint(); + + final String uri = "/modules/ietf-interfaces:interfaces/interface/0/yang-ext:mount/"; + + Response response = target(uri).request("application/yang.api+json").get(); + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + + assertTrue( + "module1-behind-mount-point in json wasn't found", + prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", + responseBody).find()); + assertTrue( + "module2-behind-mount-point in json wasn't found", + prepareJsonRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", + responseBody).find()); + + response = target(uri).request("application/yang.api+xml").get(); + assertEquals(200, response.getStatus()); + validateModulesResponseXml(response, schemaContextBehindMountPoint); + + } + + // /restconf/modules/module/pathToMountPoint/yang-ext:mount/moduleName/revision + @Test + public void getModuleBehindMountPoint() throws Exception { + setControllerContext(schemaContextModules); + + mockMountPoint(); + + final String uri = "/modules/module/ietf-interfaces:interfaces/interface/0/yang-ext:mount/" + + "module1-behind-mount-point/2014-02-03"; + + Response response = target(uri).request("application/yang.api+json").get(); + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + + assertTrue( + "module1-behind-mount-point in json wasn't found", + prepareJsonRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", + responseBody).find()); + final String[] split = responseBody.split("\"module\""); + assertEquals("\"module\" element is returned more then once", 2, split.length); + + response = target(uri).request("application/yang.api+xml").get(); + assertEquals(200, response.getStatus()); + final Document responseXml = response.readEntity(Document.class); + + final QName module = assertedModuleXmlToModuleQName(responseXml.getDocumentElement()); + + assertEquals("module1-behind-mount-point", module.getLocalName()); + assertEquals("2014-02-03", module.getRevision().get().toString()); + assertEquals("module:1:behind:mount:point", module.getNamespace().toString()); + } + + private static void validateModulesResponseXml(final Response response, final SchemaContext schemaContext) { + assertEquals(200, response.getStatus()); + final Document responseBody = response.readEntity(Document.class); + final NodeList moduleNodes = responseBody.getDocumentElement().getElementsByTagNameNS(RESTCONF_NS, "module"); + + assertTrue(moduleNodes.getLength() > 0); + + final HashSet<QName> foundModules = new HashSet<>(); + + for (int i = 0; i < moduleNodes.getLength(); i++) { + final org.w3c.dom.Node module = moduleNodes.item(i); + + final QName name = assertedModuleXmlToModuleQName(module); + foundModules.add(name); + } + + assertAllModules(foundModules,schemaContext); + } + + private static void assertAllModules(final Set<QName> foundModules, final SchemaContext schemaContext) { + for (final Module module : schemaContext.getModules()) { + final QName current = QName.create(module.getQNameModule(), module.getName()); + assertTrue("Module not found in response.", foundModules.contains(current)); + } + + } + + private static QName assertedModuleXmlToModuleQName(final org.w3c.dom.Node module) { + assertEquals("module", module.getLocalName()); + assertEquals(RESTCONF_NS, module.getNamespaceURI()); + String revision = null; + String namespace = null; + String name = null; + + + final NodeList childNodes = module.getChildNodes(); + + for (int i = 0; i < childNodes.getLength(); i++) { + final org.w3c.dom.Node child = childNodes.item(i); + assertEquals(RESTCONF_NS, child.getNamespaceURI()); + + switch (child.getLocalName()) { + case "name": + assertNull("Name element appeared multiple times", name); + name = child.getTextContent().trim(); + break; + case "revision": + assertNull("Revision element appeared multiple times", revision); + revision = child.getTextContent().trim(); + break; + case "namespace": + assertNull("Namespace element appeared multiple times", namespace); + namespace = child.getTextContent().trim(); + break; + default: + break; + } + } + + assertNotNull("Revision was not part of xml",revision); + assertNotNull("Module namespace was not part of xml",namespace); + assertNotNull("Module identiffier was not part of xml",name); + + return QName.create(namespace,revision,name); + } + + private static void validateModulesResponseJson(final Response response) { + assertEquals(200, response.getStatus()); + final String responseBody = response.readEntity(String.class); + + assertTrue("Module1 in json wasn't found", prepareJsonRegex("module1", "2014-01-01", "module:1", responseBody) + .find()); + assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody) + .find()); + assertTrue("Module3 in json wasn't found", prepareJsonRegex("module3", "2014-01-03", "module:3", responseBody) + .find()); + } + + private static Matcher prepareJsonRegex(final String module, final String revision, final String namespace, + final String searchIn) { + final StringBuilder regex = new StringBuilder(); + regex.append("^"); + + regex.append(".*\\{"); + regex.append(".*\"name\""); + regex.append(".*:"); + regex.append(".*\"" + module + "\","); + + regex.append(".*\"revision\""); + regex.append(".*:"); + regex.append(".*\"" + revision + "\","); + + regex.append(".*\"namespace\""); + regex.append(".*:"); + regex.append(".*\"" + namespace + "\""); + + regex.append(".*\\}"); + + regex.append(".*"); + regex.append("$"); + final Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL); + return ptrn.matcher(searchIn); + + } + + + private int get(final String uri, final String mediaType) { + return target(uri).request(mediaType).get().getStatus(); + } + + /** + * Container structure. + * + * <p> + * container cont { + * container cont1 { + * leaf lf11 { + * type string; + * } + */ + private static NormalizedNode prepareCnDataForMountPointTest(final boolean wrapToCont) throws Exception { + final String testModuleDate = "2014-01-09"; + final ContainerNode contChild = Builders + .containerBuilder() + .withNodeIdentifier(TestUtils.getNodeIdentifier("cont1", "test:module", testModuleDate)) + .withChild( + Builders.leafBuilder() + .withNodeIdentifier(TestUtils.getNodeIdentifier("lf11", "test:module", testModuleDate)) + .withValue("lf11 value").build()).build(); + + if (wrapToCont) { + return Builders.containerBuilder() + .withNodeIdentifier(TestUtils.getNodeIdentifier("cont", "test:module", testModuleDate)) + .withChild(contChild).build(); + } + return contChild; + + } + + private void mockReadOperationalDataMethod() { + when(brokerFacade.readOperationalData(any(YangInstanceIdentifier.class))).thenReturn(answerFromGet); + } + + private void mockReadConfigurationDataMethod() { + when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class), isNull())) + .thenReturn(answerFromGet); + } + + private static NormalizedNode prepareCnDataForSlashesBehindMountPointTest() throws Exception { + return ImmutableMapEntryNodeBuilder.create() + .withNodeIdentifier( + TestUtils.getNodeIdentifierPredicate("lst1", "test:module", "2014-01-09", "lf11", + "GigabitEthernet0/0/0/0")) + .withChild( + ImmutableLeafNodeBuilder.create() + .withNodeIdentifier(TestUtils.getNodeIdentifier("lf11", "test:module", "2014-01-09")) + .withValue("GigabitEthernet0/0/0/0").build()).build(); + + } + + /** + * If includeWhiteChars URI parameter is set to false then no white characters can be included in returned output. + */ + @Test + public void getDataWithUriIncludeWhiteCharsParameterTest() throws Exception { + getDataWithUriIncludeWhiteCharsParameter("config"); + getDataWithUriIncludeWhiteCharsParameter("operational"); + } + + private void getDataWithUriIncludeWhiteCharsParameter(final String target) throws Exception { + mockReadConfigurationDataMethod(); + mockReadOperationalDataMethod(); + final String uri = "/" + target + "/ietf-interfaces:interfaces/interface/eth0"; + Response response = target(uri).queryParam("prettyPrint", "false").request("application/xml").get(); + final String xmlData = response.readEntity(String.class); + + Pattern pattern = Pattern.compile(".*(>\\s+|\\s+<).*", Pattern.DOTALL); + Matcher matcher = pattern.matcher(xmlData); + // XML element can't surrounded with white character (e.g "> " or + // " <") + assertFalse(matcher.matches()); + + response = target(uri).queryParam("prettyPrint", "false").request("application/json").get(); + final String jsonData = response.readEntity(String.class); + pattern = Pattern.compile(".*(\\}\\s+|\\s+\\{|\\]\\s+|\\s+\\[|\\s+:|:\\s+).*", Pattern.DOTALL); + matcher = pattern.matcher(jsonData); + // JSON element can't surrounded with white character (e.g "} ", " {", + // "] ", " [", " :" or ": ") + assertFalse(matcher.matches()); + } + + /** + * Tests behavior when invalid value of depth URI parameter. + */ + @Test + @Ignore + public void getDataWithInvalidDepthParameterTest() { + setControllerContext(schemaContextModules); + + final MultivaluedMap<String, String> paramMap = new MultivaluedHashMap<>(); + paramMap.putSingle("depth", "1o"); + final UriInfo mockInfo = mock(UriInfo.class); + when(mockInfo.getQueryParameters(false)).thenAnswer(invocation -> paramMap); + + getDataWithInvalidDepthParameterTest(mockInfo); + + paramMap.putSingle("depth", "0"); + getDataWithInvalidDepthParameterTest(mockInfo); + + paramMap.putSingle("depth", "-1"); + getDataWithInvalidDepthParameterTest(mockInfo); + } + + private void getDataWithInvalidDepthParameterTest(final UriInfo uriInfo) { + try { + final QName qNameDepth1Cont = QName.create("urn:nested:module", "2014-06-3", "depth1-cont"); + final YangInstanceIdentifier ii = YangInstanceIdentifier.builder().node(qNameDepth1Cont).build(); + final NormalizedNode value = + Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(qNameDepth1Cont)).build(); + when(brokerFacade.readConfigurationData(eq(ii))).thenReturn(value); + restconfImpl.readConfigurationData("nested-module:depth1-cont", uriInfo); + fail("Expected RestconfDocumentedException"); + } catch (final RestconfDocumentedException e) { + assertTrue("Unexpected error message: " + e.getErrors().get(0).getErrorMessage(), e.getErrors().get(0) + .getErrorMessage().contains("depth")); + } + } + + @SuppressWarnings("unused") + private void verifyXMLResponse(final Response response, final NodeData nodeData) { + final Document doc = response.readEntity(Document.class); + assertNotNull("Could not parse XML document", doc); + + verifyContainerElement(doc.getDocumentElement(), nodeData); + } + + @SuppressWarnings("unchecked") + private void verifyContainerElement(final Element element, final NodeData nodeData) { + + assertEquals("Element local name", nodeData.key, element.getLocalName()); + + final NodeList childNodes = element.getChildNodes(); + if (nodeData.data == null) { // empty container + assertTrue( + "Expected no child elements for \"" + element.getLocalName() + "\"", childNodes.getLength() == 0); + return; + } + + final Map<String, NodeData> expChildMap = new HashMap<>(); + for (final NodeData expChild : (List<NodeData>) nodeData.data) { + expChildMap.put(expChild.key.toString(), expChild); + } + + for (int i = 0; i < childNodes.getLength(); i++) { + final org.w3c.dom.Node actualChild = childNodes.item(i); + if (!(actualChild instanceof Element)) { + continue; + } + + final Element actualElement = (Element) actualChild; + final NodeData expChild = expChildMap.remove(actualElement.getLocalName()); + assertNotNull( + "Unexpected child element for parent \"" + element.getLocalName() + "\": " + + actualElement.getLocalName(), expChild); + + if (expChild.data == null || expChild.data instanceof List) { + verifyContainerElement(actualElement, expChild); + } else { + assertEquals("Text content for element: " + actualElement.getLocalName(), expChild.data, + actualElement.getTextContent()); + } + } + + if (!expChildMap.isEmpty()) { + fail("Missing elements for parent \"" + element.getLocalName() + "\": " + expChildMap.keySet()); + } + } + + private void mockMountPoint() { + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextBehindMountPoint))); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestOperationUtils.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestOperationUtils.java new file mode 100644 index 0000000..bdc9679 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestOperationUtils.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +public final class RestOperationUtils { + + public static final String JSON = "+json"; + public static final String XML = "+xml"; + + private RestOperationUtils() { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java new file mode 100644 index 0000000..3aa6a9c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.opendaylight.controller.sal.restconf.impl.test.RestOperationUtils.XML; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.FluentFuture; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.text.ParseException; +import java.util.Optional; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +public class RestPostOperationTest extends JerseyTest { + + private static String xmlBlockData; + private static String xmlData3; + private static String xmlData4; + + private static EffectiveModelContext schemaContextYangsIetf; + private static EffectiveModelContext schemaContextTestModule; + private static EffectiveModelContext schemaContext; + + private BrokerFacade brokerFacade; + private RestconfImpl restconfImpl; + private ControllerContext controllerContext; + private DOMMountPoint mountInstance; + + @BeforeClass + public static void init() throws URISyntaxException, IOException { + schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs"); + schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module"); + schemaContext = TestUtils.loadSchemaContext("/test-config-data/yang1"); + loadData(); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + + mountInstance = mock(DOMMountPoint.class); + controllerContext = TestRestconfUtils.newControllerContext(schemaContext, mountInstance); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfImpl, + new XmlNormalizedNodeBodyReader(controllerContext), new NormalizedNodeXmlBodyWriter(), + new JsonNormalizedNodeBodyReader(controllerContext), new NormalizedNodeJsonBodyWriter(), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + private void setSchemaControllerContext(final EffectiveModelContext schema) { + controllerContext.setSchemas(schema); + } + + @SuppressWarnings("unchecked") + @Test + @Ignore /// xmlData* need netconf-yang + public void postDataViaUrlMountPoint() throws UnsupportedEncodingException { + setSchemaControllerContext(schemaContextYangsIetf); + when(brokerFacade.commitConfigurationDataPost(any(DOMMountPoint.class), any(YangInstanceIdentifier.class), + any(NormalizedNode.class), null, null)).thenReturn(mock(FluentFuture.class)); + + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextTestModule))); + + String uri = "/config/ietf-interfaces:interfaces/interface/0/"; + assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData4)); + uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont"; + assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData3)); + + assertEquals(400, post(uri, MediaType.APPLICATION_JSON, "")); + } + + @SuppressWarnings("unchecked") + @Test + @Ignore //jenkins has problem with JerseyTest + // - we expecting problems with singletons ControllerContext as schemaContext holder + public void createConfigurationDataTest() throws UnsupportedEncodingException, ParseException { + when(brokerFacade.commitConfigurationDataPost((EffectiveModelContext) null, any(YangInstanceIdentifier.class), + any(NormalizedNode.class), null, null)) + .thenReturn(mock(FluentFuture.class)); + + final ArgumentCaptor<YangInstanceIdentifier> instanceIdCaptor = + ArgumentCaptor.forClass(YangInstanceIdentifier.class); + final ArgumentCaptor<NormalizedNode> compNodeCaptor = ArgumentCaptor.forClass(NormalizedNode.class); + + + // FIXME : identify who is set the schemaContext +// final String URI_1 = "/config"; +// assertEquals(204, post(URI_1, Draft02.MediaTypes.DATA + XML, xmlTestInterface)); +// verify(brokerFacade).commitConfigurationDataPost(instanceIdCaptor.capture(), compNodeCaptor.capture()); + final String identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces]"; +// assertEquals(identifier, ImmutableList.copyOf(instanceIdCaptor.getValue().getPathArguments()).toString()); + + final String URI_2 = "/config/test-interface:interfaces"; + assertEquals(204, post(URI_2, Draft02.MediaTypes.DATA + XML, xmlBlockData)); + // FIXME : NEVER test a nr. of call some service in complex test suite +// verify(brokerFacade, times(2)) + verify(brokerFacade, times(1)) + .commitConfigurationDataPost((EffectiveModelContext) null, instanceIdCaptor.capture(), + compNodeCaptor.capture(), null, null); +// identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces," + +// "(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)block]"; + assertEquals(identifier, ImmutableList.copyOf(instanceIdCaptor.getValue().getPathArguments()).toString()); + } + + @Test + public void createConfigurationDataNullTest() throws UnsupportedEncodingException { + doReturn(CommitInfo.emptyFluentFuture()).when(brokerFacade).commitConfigurationDataPost( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), isNull(), + isNull()); + + //FIXME : find who is set schemaContext +// final String URI_1 = "/config"; +// assertEquals(204, post(URI_1, Draft02.MediaTypes.DATA + XML, xmlTestInterface)); + + final String URI_2 = "/config/test-interface:interfaces"; + assertEquals(204, post(URI_2, Draft02.MediaTypes.DATA + XML, xmlBlockData)); + } + + private int post(final String uri, final String mediaType, final String data) { + return target(uri).request(mediaType).post(Entity.entity(data, mediaType)).getStatus(); + } + + private static void loadData() throws IOException, URISyntaxException { + final String xmlPathBlockData = + RestconfImplTest.class.getResource("/test-config-data/xml/block-data.xml").getPath(); + xmlBlockData = TestUtils.loadTextFile(xmlPathBlockData); + final String data3Input = RestconfImplTest.class.getResource("/full-versions/test-data2/data3.xml").getPath(); + xmlData3 = TestUtils.loadTextFile(data3Input); + final String data4Input = RestconfImplTest.class.getResource("/full-versions/test-data2/data7.xml").getPath(); + xmlData4 = TestUtils.loadTextFile(data4Input); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutConfigTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutConfigTest.java new file mode 100644 index 0000000..9cd03d8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutConfigTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.FileNotFoundException; +import java.util.HashSet; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.PutResult; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +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.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; + +@RunWith(MockitoJUnitRunner.class) +public class RestPutConfigTest { + + private static EffectiveModelContext schemaContext; + private RestconfImpl restconfService; + private ControllerContext controllerCx; + + @Mock + private BrokerFacade brokerFacade; + + @BeforeClass + public static void staticInit() throws FileNotFoundException { + schemaContext = TestRestconfUtils.loadSchemaContext("/test-config-data/yang1/", null); + } + + @Before + public void init() { + controllerCx = TestRestconfUtils.newControllerContext(schemaContext); + restconfService = RestconfImpl.newInstance(brokerFacade, controllerCx); + } + + @Test + public void testPutConfigData() { + final String identifier = "test-interface:interfaces/interface/key"; + final InstanceIdentifierContext iiCx = controllerCx.toInstanceIdentifier(identifier); + final MapEntryNode data = Mockito.mock(MapEntryNode.class); + final QName qName = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "interface"); + final QName qNameKey = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "name"); + final NodeIdentifierWithPredicates identWithPredicates = + NodeIdentifierWithPredicates.of(qName, qNameKey, "key"); + Mockito.when(data.getIdentifier()).thenReturn(identWithPredicates); + final NormalizedNodeContext payload = new NormalizedNodeContext(iiCx, data); + + mockingBrokerPut(iiCx.getInstanceIdentifier(), data); + + final UriInfo uriInfo = Mockito.mock(UriInfo.class); + final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class); + Mockito.when(value.entrySet()).thenReturn(new HashSet<>()); + Mockito.when(uriInfo.getQueryParameters()).thenReturn(value); + restconfService.updateConfigurationData(identifier, payload, uriInfo); + } + + @Test + public void testPutConfigDataCheckOnlyLastElement() { + final String identifier = "test-interface:interfaces/interface/key/sub-interface/subkey"; + final InstanceIdentifierContext iiCx = controllerCx.toInstanceIdentifier(identifier); + final MapEntryNode data = Mockito.mock(MapEntryNode.class); + final QName qName = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "sub-interface"); + final QName qNameSubKey = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "sub-name"); + final NodeIdentifierWithPredicates identWithPredicates = + NodeIdentifierWithPredicates.of(qName, qNameSubKey, "subkey"); + Mockito.when(data.getIdentifier()).thenReturn(identWithPredicates); + final NormalizedNodeContext payload = new NormalizedNodeContext(iiCx, data); + + mockingBrokerPut(iiCx.getInstanceIdentifier(), data); + + final UriInfo uriInfo = Mockito.mock(UriInfo.class); + final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class); + Mockito.when(value.entrySet()).thenReturn(new HashSet<>()); + Mockito.when(uriInfo.getQueryParameters()).thenReturn(value); + restconfService.updateConfigurationData(identifier, payload, uriInfo); + } + + @Test(expected = RestconfDocumentedException.class) + public void testPutConfigDataMissingUriKey() { + final String identifier = "test-interface:interfaces/interface"; + controllerCx.toInstanceIdentifier(identifier); + } + + @Test(expected = RestconfDocumentedException.class) + public void testPutConfigDataDiferentKey() { + final String identifier = "test-interface:interfaces/interface/key"; + final InstanceIdentifierContext iiCx = controllerCx.toInstanceIdentifier(identifier); + final MapEntryNode data = Mockito.mock(MapEntryNode.class); + final QName qName = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "interface"); + final QName qNameKey = QName.create("urn:ietf:params:xml:ns:yang:test-interface", "2014-07-01", "name"); + final NodeIdentifierWithPredicates identWithPredicates = + NodeIdentifierWithPredicates.of(qName, qNameKey, "notSameKey"); + Mockito.when(data.getIdentifier()).thenReturn(identWithPredicates); + final NormalizedNodeContext payload = new NormalizedNodeContext(iiCx, data); + + mockingBrokerPut(iiCx.getInstanceIdentifier(), data); + + final UriInfo uriInfo = Mockito.mock(UriInfo.class); + final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class); + Mockito.when(value.entrySet()).thenReturn(new HashSet<>()); + Mockito.when(uriInfo.getQueryParameters()).thenReturn(value); + restconfService.updateConfigurationData(identifier, payload, uriInfo); + } + + private void mockingBrokerPut(final YangInstanceIdentifier yii, final NormalizedNode data) { + final PutResult result = Mockito.mock(PutResult.class); + Mockito.when(brokerFacade.commitConfigurationDataPut(schemaContext, yii, data, null, null)) + .thenReturn(result); + Mockito.doReturn(CommitInfo.emptyFluentFuture()).when(result).getFutureOfPutData(); + Mockito.when(result.getStatus()).thenReturn(Status.OK); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java new file mode 100644 index 0000000..8c04e50 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.util.Optional; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.CommitInfo; +import org.opendaylight.mdsal.common.api.OptimisticLockFailedException; +import org.opendaylight.mdsal.common.api.TransactionCommitFailedException; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.PutResult; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +//TODO UNSTABLE TESTS - FIX ME +@Ignore +public class RestPutOperationTest extends JerseyTest { + + private static String xmlData; + private static String xmlData2; + private static String xmlData3; + + private static EffectiveModelContext schemaContextYangsIetf; + private static EffectiveModelContext schemaContextTestModule; + + private BrokerFacade brokerFacade; + private RestconfImpl restconfImpl; + private DOMMountPoint mountInstance; + + @BeforeClass + public static void init() throws IOException, ReactorException { + schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs"); + schemaContextTestModule = TestUtils.loadSchemaContext("/full-versions/test-module"); + loadData(); + } + + private static void loadData() throws IOException { + final InputStream xmlStream = + RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml"); + xmlData = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream)); + final InputStream xmlStream2 = + RestconfImplTest.class.getResourceAsStream("/full-versions/test-data2/data2.xml"); + xmlData2 = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream2)); + final InputStream xmlStream3 = + RestconfImplTest.class.getResourceAsStream("/full-versions/test-data2/data7.xml"); + xmlData3 = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream3)); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + + mountInstance = mock(DOMMountPoint.class); + final ControllerContext controllerContext = + TestRestconfUtils.newControllerContext(schemaContextYangsIetf, mountInstance); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfImpl, + new XmlNormalizedNodeBodyReader(controllerContext), new NormalizedNodeXmlBodyWriter(), + new JsonNormalizedNodeBodyReader(controllerContext), new NormalizedNodeJsonBodyWriter(), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + /** + * Tests of status codes for "/config/{identifier}". + */ + @Test + public void putConfigStatusCodes() throws UnsupportedEncodingException { + final String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + mockCommitConfigurationDataPutMethod(true); + assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + + mockCommitConfigurationDataPutMethod(false); + assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + + assertEquals(400, put(uri, MediaType.APPLICATION_JSON, "")); + } + + @Test + public void putConfigStatusCodesEmptyBody() throws UnsupportedEncodingException { + final String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + @SuppressWarnings("unused") + final Response resp = target(uri).request(MediaType.APPLICATION_JSON).put( + Entity.entity("", MediaType.APPLICATION_JSON)); + assertEquals(400, put(uri, MediaType.APPLICATION_JSON, "")); + } + + @Test + public void testRpcResultCommitedToStatusCodesWithMountPoint() throws UnsupportedEncodingException, + FileNotFoundException, URISyntaxException { + final PutResult result = mock(PutResult.class); + when(brokerFacade.commitMountPointDataPut(any(DOMMountPoint.class), any(YangInstanceIdentifier.class), + any(NormalizedNode.class), null, null)).thenReturn(result); + doReturn(CommitInfo.emptyFluentFuture()).when(result).getFutureOfPutData(); + when(result.getStatus()).thenReturn(Status.OK); + + mockMountPoint(); + + String uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont"; + assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData2)); + + uri = "/config/ietf-interfaces:interfaces/yang-ext:mount/test-module:cont"; + assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData2)); + } + + @Test + public void putDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException { + final PutResult result = mock(PutResult.class); + doReturn(result).when(brokerFacade).commitMountPointDataPut(any(DOMMountPoint.class), + any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null); + doReturn(CommitInfo.emptyFluentFuture()).when(result).getFutureOfPutData(); + when(result.getStatus()).thenReturn(Status.OK); + + mockMountPoint(); + + final String uri = "/config/ietf-interfaces:interfaces/yang-ext:mount"; + assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData3)); + } + + @Test + public void putWithOptimisticLockFailedException() throws UnsupportedEncodingException { + + final String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + + doThrow(OptimisticLockFailedException.class).when(brokerFacade).commitConfigurationDataPut( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, + null); + + assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + + doThrow(OptimisticLockFailedException.class).doReturn(mock(PutResult.class)).when(brokerFacade) + .commitConfigurationDataPut(any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), + any(NormalizedNode.class), null, null); + + assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + } + + @Test + public void putWithTransactionCommitFailedException() throws UnsupportedEncodingException { + + final String uri = "/config/ietf-interfaces:interfaces/interface/eth0"; + + doThrow(TransactionCommitFailedException.class) + .when(brokerFacade).commitConfigurationDataPut( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), + null, null); + + assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData)); + } + + private int put(final String uri, final String mediaType, final String data) throws UnsupportedEncodingException { + return target(uri).request(mediaType).put(Entity.entity(data, mediaType)).getStatus(); + } + + private void mockMountPoint() { + when(mountInstance.getService(DOMSchemaService.class)) + .thenReturn(Optional.of(FixedDOMSchemaService.of(schemaContextTestModule))); + } + + private void mockCommitConfigurationDataPutMethod(final boolean noErrors) { + final PutResult putResMock = mock(PutResult.class); + if (noErrors) { + doReturn(putResMock).when(brokerFacade).commitConfigurationDataPut( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), + null, null); + } else { + doThrow(RestconfDocumentedException.class).when(brokerFacade).commitConfigurationDataPut( + any(EffectiveModelContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), + null, null); + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java new file mode 100644 index 0000000..f4ddb35 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +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 static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Iterators; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; +import javax.xml.namespace.NamespaceContext; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.api.RestconfService; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +/** + * Unit tests for RestconfDocumentedExceptionMapper. + * + * @author Thomas Pantelis + */ +public class RestconfDocumentedExceptionMapperTest extends JerseyTest { + + interface ErrorInfoVerifier { + void verifyXML(Node errorInfoNode); + + void verifyJson(JsonElement errorInfoElement); + } + + static class SimpleErrorInfoVerifier implements ErrorInfoVerifier { + + String expTextContent; + + SimpleErrorInfoVerifier(final String expErrorInfo) { + expTextContent = expErrorInfo; + } + + void verifyContent(final String actualContent) { + assertNotNull("Actual \"error-info\" text content is null", actualContent); + assertTrue("", actualContent.contains(expTextContent)); + } + + @Override + public void verifyXML(final Node errorInfoNode) { + verifyContent(errorInfoNode.getTextContent()); + } + + @Override + public void verifyJson(final JsonElement errorInfoElement) { + verifyContent(errorInfoElement.getAsString()); + } + } + + private static final Logger LOG = LoggerFactory.getLogger(RestconfDocumentedExceptionMapperTest.class); + private static final String IETF_RESTCONF = "ietf-restconf"; + static RestconfService mockRestConf = mock(RestconfService.class); + + static XPath XPATH = XPathFactory.newInstance().newXPath(); + static XPathExpression ERROR_LIST; + static XPathExpression ERROR_TYPE; + static XPathExpression ERROR_TAG; + static XPathExpression ERROR_MESSAGE; + static XPathExpression ERROR_APP_TAG; + static XPathExpression ERROR_INFO; + + private static EffectiveModelContext schemaContext; + + @BeforeClass + public static void init() throws Exception { + schemaContext = TestUtils.loadSchemaContext("/modules"); + + final NamespaceContext nsContext = new NamespaceContext() { + @Override + public Iterator<String> getPrefixes(final String namespaceURI) { + return Iterators.singletonIterator(IETF_RESTCONF); + } + + @Override + public String getPrefix(final String namespaceURI) { + return null; + } + + @Override + public String getNamespaceURI(final String prefix) { + return IETF_RESTCONF.equals(prefix) ? Draft02.RestConfModule.NAMESPACE : null; + } + }; + + XPATH.setNamespaceContext(nsContext); + ERROR_LIST = XPATH.compile("ietf-restconf:errors/ietf-restconf:error"); + ERROR_TYPE = XPATH.compile("ietf-restconf:error-type"); + ERROR_TAG = XPATH.compile("ietf-restconf:error-tag"); + ERROR_MESSAGE = XPATH.compile("ietf-restconf:error-message"); + ERROR_APP_TAG = XPATH.compile("ietf-restconf:error-app-tag"); + ERROR_INFO = XPATH.compile("ietf-restconf:error-info"); + } + + @Override + @Before + public void setUp() throws Exception { + reset(mockRestConf); + super.setUp(); + } + + @Override + protected Application configure() { + ResourceConfig resourceConfig = new ResourceConfig(); + ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + resourceConfig = resourceConfig.registerInstances(mockRestConf, + new XmlNormalizedNodeBodyReader(controllerContext), new JsonNormalizedNodeBodyReader(controllerContext), + new NormalizedNodeJsonBodyWriter(), new NormalizedNodeXmlBodyWriter(), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + void stageMockEx(final RestconfDocumentedException ex) { + reset(mockRestConf); + when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenThrow(ex); + } + + void testJsonResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType, + final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag, + final ErrorInfoVerifier errorInfoVerifier) throws Exception { + + stageMockEx(ex); + + final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get(); + + final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, expStatus); + + verifyJsonResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier); + } + + @Test + public void testToJsonResponseWithMessageOnly() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR, + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null); + + + // To test verification code + // String json = + // "{ errors: {" + + // " error: [{" + + // " error-tag : \"operation-failed\"" + + // " ,error-type : \"application\"" + + // " ,error-message : \"An error occurred\"" + + // " ,error-info : {" + + // " session-id: \"123\"" + + // " ,address: \"1.2.3.4\"" + + // " }" + + // " }]" + + // " }" + + // "}"; + // + // verifyJsonResponseBody( new java.io.StringBufferInputStream(json ), + // ErrorType.APPLICATION, + // ErrorTag.OPERATION_FAILED, "An error occurred", null, + // com.google.common.collect.ImmutableMap.of( "session-id", "123", + // "address", "1.2.3.4" ) ); + } + + @Test + public void testToJsonResponseWithInUseErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithInvalidValueErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE), + Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null); + + } + + @Test + public void testToJsonResponseWithTooBigErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG), + Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null); + + } + + @Test + public void testToJsonResponseWithMissingAttributeErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithBadAttributeErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithUnknownAttributeErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithBadElementErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithUnknownElementErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithUnknownNamespaceErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithMalformedMessageErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithAccessDeniedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED), + Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithLockDeniedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithResourceDeniedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithRollbackFailedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithDataExistsErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithDataMissingErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithOperationNotSupportedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, + ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL, + ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithOperationFailedErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithPartialOperationErrorTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null); + } + + @Test + public void testToJsonResponseWithErrorAppTag() throws Exception { + + testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null); + } + + @Test + @Ignore // FIXME : find why it return "error-type" RPC no expected APPLICATION + public void testToJsonResponseWithMultipleErrors() throws Exception { + + final List<RestconfError> errorList = Arrays.asList( + new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1"), + new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2")); + stageMockEx(new RestconfDocumentedException("mock", null, errorList)); + + final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get(); + + final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.CONFLICT); + + final JsonArray arrayElement = parseJsonErrorArrayElement(stream); + + assertEquals("\"error\" Json array element length", 2, arrayElement.size()); + + verifyJsonErrorNode( + arrayElement.get(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null); + + verifyJsonErrorNode(arrayElement.get(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null); + } + + @Test + public void testToJsonResponseWithErrorInfo() throws Exception { + + final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>"; + testJsonResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST, + ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", + new SimpleErrorInfoVerifier(errorInfo)); + } + + @Test + public void testToJsonResponseWithExceptionCause() throws Exception { + + final Exception cause = new Exception("mock exception cause"); + testJsonResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR, + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, + new SimpleErrorInfoVerifier(cause.getMessage())); + } + + void testXMLResponse(final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType, + final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag, + final ErrorInfoVerifier errorInfoVerifier) throws Exception { + stageMockEx(ex); + + final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get(); + + final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, expStatus); + + verifyXMLResponseBody(stream, expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, errorInfoVerifier); + } + + @Test + public void testToXMLResponseWithMessageOnly() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error"), Status.INTERNAL_SERVER_ERROR, + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null); + + // To test verification code + // String xml = + // "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">"+ + // " <error>" + + // " <error-type>application</error-type>"+ + // " <error-tag>operation-failed</error-tag>"+ + // " <error-message>An error occurred</error-message>"+ + // " <error-info>" + + // " <session-id>123</session-id>" + + // " <address>1.2.3.4</address>" + + // " </error-info>" + + // " </error>" + + // "</errors>"; + // + // verifyXMLResponseBody( new java.io.StringBufferInputStream(xml), + // ErrorType.APPLICATION, + // ErrorTag.OPERATION_FAILED, "An error occurred", null, + // com.google.common.collect.ImmutableMap.of( "session-id", "123", + // "address", "1.2.3.4" ) ); + } + + @Test + public void testToXMLResponseWithInUseErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.IN_USE), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.IN_USE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithInvalidValueErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.RPC, ErrorTag.INVALID_VALUE), + Status.BAD_REQUEST, ErrorType.RPC, ErrorTag.INVALID_VALUE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithTooBigErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.TRANSPORT, ErrorTag.TOO_BIG), + Status.REQUEST_ENTITY_TOO_LARGE, ErrorType.TRANSPORT, ErrorTag.TOO_BIG, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithMissingAttributeErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MISSING_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithBadAttributeErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithUnknownAttributeErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ATTRIBUTE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithBadElementErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.BAD_ELEMENT, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithUnknownElementErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithUnknownNamespaceErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithMalformedMessageErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE), + Status.BAD_REQUEST, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithAccessDeniedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED), + Status.FORBIDDEN, ErrorType.PROTOCOL, ErrorTag.ACCESS_DENIED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithLockDeniedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.LOCK_DENIED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithResourceDeniedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.RESOURCE_DENIED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithRollbackFailedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.ROLLBACK_FAILED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithDataExistsErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithDataMissingErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING), + Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithOperationNotSupportedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, + ErrorTag.OPERATION_NOT_SUPPORTED), Status.NOT_IMPLEMENTED, ErrorType.PROTOCOL, + ErrorTag.OPERATION_NOT_SUPPORTED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithOperationFailedErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.OPERATION_FAILED, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithPartialOperationErrorTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION), + Status.INTERNAL_SERVER_ERROR, ErrorType.PROTOCOL, ErrorTag.PARTIAL_OPERATION, "mock error", null, null); + } + + @Test + public void testToXMLResponseWithErrorAppTag() throws Exception { + + testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag")), Status.BAD_REQUEST, ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", null); + } + + @Test + public void testToXMLResponseWithErrorInfo() throws Exception { + + final String errorInfo = "<address>1.2.3.4</address> <session-id>123</session-id>"; + testXMLResponse(new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, + ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", errorInfo)), Status.BAD_REQUEST, + ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, "mock error", "mock-app-tag", + new SimpleErrorInfoVerifier(errorInfo)); + } + + @Test + public void testToXMLResponseWithExceptionCause() throws Exception { + + final Exception cause = new Exception("mock exception cause"); + testXMLResponse(new RestconfDocumentedException("mock error", cause), Status.INTERNAL_SERVER_ERROR, + ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, + new SimpleErrorInfoVerifier(cause.getMessage())); + } + + @Test + @Ignore // FIXME : find why it return error-type as RPC no APPLICATION + public void testToXMLResponseWithMultipleErrors() throws Exception { + + final List<RestconfError> errorList = Arrays.asList( + new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1"), + new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2")); + stageMockEx(new RestconfDocumentedException("mock", null, errorList)); + + final Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get(); + + final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_XML, Status.CONFLICT); + + final Document doc = parseXMLDocument(stream); + + final NodeList children = getXMLErrorList(doc, 2); + + verifyXMLErrorNode(children.item(0), ErrorType.APPLICATION, ErrorTag.LOCK_DENIED, "mock error1", null, null); + + verifyXMLErrorNode(children.item(1), ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2", null, null); + } + + @Test + public void testToResponseWithAcceptHeader() throws Exception { + + stageMockEx(new RestconfDocumentedException("mock error")); + + final Response resp = target("/operational/foo").request().header("Accept", MediaType.APPLICATION_JSON).get(); + + final InputStream stream = verifyResponse(resp, MediaType.APPLICATION_JSON, Status.INTERNAL_SERVER_ERROR); + + verifyJsonResponseBody(stream, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, "mock error", null, null); + } + + @Test + @Ignore + public void testToResponseWithStatusOnly() throws Exception { + + // The StructuredDataToJsonProvider should throw a + // RestconfDocumentedException with no data + + when(mockRestConf.readOperationalData(any(String.class), any(UriInfo.class))).thenReturn( + new NormalizedNodeContext(null, null)); + + final Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get(); + + verifyResponse(resp, MediaType.TEXT_PLAIN, Status.NOT_FOUND); + } + + InputStream verifyResponse(final Response resp, final String expMediaType, final Status expStatus) { + assertEquals("getMediaType", MediaType.valueOf(expMediaType), resp.getMediaType()); + assertEquals("getStatus", expStatus.getStatusCode(), resp.getStatus()); + + final Object entity = resp.getEntity(); + assertEquals("Response entity", true, entity instanceof InputStream); + final InputStream stream = (InputStream) entity; + return stream; + } + + void verifyJsonResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag, + final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier) + throws Exception { + + final JsonArray arrayElement = parseJsonErrorArrayElement(stream); + + assertEquals("\"error\" Json array element length", 1, arrayElement.size()); + + verifyJsonErrorNode(arrayElement.get(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, + errorInfoVerifier); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private static JsonArray parseJsonErrorArrayElement(final InputStream stream) throws IOException { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + stream.transferTo(bos); + + LOG.info("JSON: {}", bos); + + JsonElement rootElement; + try { + rootElement = JsonParser.parseReader(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray()))); + } catch (final Exception e) { + throw new IllegalArgumentException("Invalid JSON response:\n" + bos.toString(), e); + } + + assertTrue("Root element of Json is not an Object", rootElement.isJsonObject()); + + final Set<Entry<String, JsonElement>> errorsEntrySet = rootElement.getAsJsonObject().entrySet(); + assertEquals("Json Object element set count", 1, errorsEntrySet.size()); + + final Entry<String, JsonElement> errorsEntry = errorsEntrySet.iterator().next(); + final JsonElement errorsElement = errorsEntry.getValue(); + assertEquals("First Json element name", "errors", errorsEntry.getKey()); + assertTrue("\"errors\" Json element is not an Object", errorsElement.isJsonObject()); + + final Set<Entry<String, JsonElement>> errorListEntrySet = errorsElement.getAsJsonObject().entrySet(); + assertEquals("Root \"errors\" element child count", 1, errorListEntrySet.size()); + + final JsonElement errorListElement = errorListEntrySet.iterator().next().getValue(); + assertEquals("\"errors\" child Json element name", "error", errorListEntrySet.iterator().next().getKey()); + assertTrue("\"error\" Json element is not an Array", errorListElement.isJsonArray()); + + // As a final check, make sure there aren't multiple "error" array + // elements. Unfortunately, + // the call above to getAsJsonObject().entrySet() will out duplicate + // "error" elements. So + // we'll use regex on the json string to verify this. + + final Matcher matcher = Pattern.compile("\"error\"[ ]*:[ ]*\\[", Pattern.DOTALL).matcher(bos.toString()); + assertTrue("Expected 1 \"error\" element", matcher.find()); + assertFalse("Found multiple \"error\" elements", matcher.find()); + + return errorListElement.getAsJsonArray(); + } + + void verifyJsonErrorNode(final JsonElement errorEntryElement, final ErrorType expErrorType, + final ErrorTag expErrorTag, final String expErrorMessage, final String expErrorAppTag, + final ErrorInfoVerifier errorInfoVerifier) { + + JsonElement errorInfoElement = null; + final Map<String, String> leafMap = new HashMap<>(); + for (final Entry<String, JsonElement> entry : errorEntryElement.getAsJsonObject().entrySet()) { + final String leafName = entry.getKey(); + final JsonElement leafElement = entry.getValue(); + + if ("error-info".equals(leafName)) { + assertNotNull("Found unexpected \"error-info\" element", errorInfoVerifier); + errorInfoElement = leafElement; + } else { + assertTrue("\"error\" leaf Json element " + leafName + " is not a Primitive", + leafElement.isJsonPrimitive()); + + leafMap.put(leafName, leafElement.getAsString()); + } + } + + assertEquals("error-type", expErrorType.elementBody(), leafMap.remove("error-type")); + assertEquals("error-tag", expErrorTag.elementBody(), leafMap.remove("error-tag")); + + verifyOptionalJsonLeaf(leafMap.remove("error-message"), expErrorMessage, "error-message"); + verifyOptionalJsonLeaf(leafMap.remove("error-app-tag"), expErrorAppTag, "error-app-tag"); + + if (!leafMap.isEmpty()) { + fail("Found unexpected Json leaf elements for \"error\" element: " + leafMap); + } + + if (errorInfoVerifier != null) { + assertNotNull("Missing \"error-info\" element", errorInfoElement); + errorInfoVerifier.verifyJson(errorInfoElement); + } + } + + void verifyOptionalJsonLeaf(final String actualValue, final String expValue, final String tagName) { + if (expValue != null) { + assertEquals(tagName, expValue, actualValue); + } else { + assertNull("Found unexpected \"error\" leaf entry for: " + tagName, actualValue); + } + } + + void verifyXMLResponseBody(final InputStream stream, final ErrorType expErrorType, final ErrorTag expErrorTag, + final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier) + throws Exception { + + final Document doc = parseXMLDocument(stream); + + final NodeList children = getXMLErrorList(doc, 1); + + verifyXMLErrorNode(children.item(0), expErrorType, expErrorTag, expErrorMessage, expErrorAppTag, + errorInfoVerifier); + } + + private static Document parseXMLDocument(final InputStream stream) throws IOException, SAXException { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + stream.transferTo(bos); + + LOG.debug("XML: {}", bos); + + return UntrustedXML.newDocumentBuilder().parse(new ByteArrayInputStream(bos.toByteArray())); + } + + void verifyXMLErrorNode(final Node errorNode, final ErrorType expErrorType, final ErrorTag expErrorTag, + final String expErrorMessage, final String expErrorAppTag, final ErrorInfoVerifier errorInfoVerifier) + throws Exception { + + final String errorType = (String) ERROR_TYPE.evaluate(errorNode, XPathConstants.STRING); + assertEquals("error-type", expErrorType.elementBody(), errorType); + + final String errorTag = (String) ERROR_TAG.evaluate(errorNode, XPathConstants.STRING); + assertEquals("error-tag", expErrorTag.elementBody(), errorTag); + + verifyOptionalXMLLeaf(errorNode, ERROR_MESSAGE, expErrorMessage, "error-message"); + verifyOptionalXMLLeaf(errorNode, ERROR_APP_TAG, expErrorAppTag, "error-app-tag"); + + final Node errorInfoNode = (Node) ERROR_INFO.evaluate(errorNode, XPathConstants.NODE); + if (errorInfoVerifier != null) { + assertNotNull("Missing \"error-info\" node", errorInfoNode); + + errorInfoVerifier.verifyXML(errorInfoNode); + } else { + assertNull("Found unexpected \"error-info\" node", errorInfoNode); + } + } + + void verifyOptionalXMLLeaf(final Node fromNode, final XPathExpression xpath, final String expValue, + final String tagName) throws Exception { + if (expValue != null) { + final String actual = (String) xpath.evaluate(fromNode, XPathConstants.STRING); + assertEquals(tagName, expValue, actual); + } else { + assertNull("Found unexpected \"error\" leaf entry for: " + tagName, + xpath.evaluate(fromNode, XPathConstants.NODE)); + } + } + + NodeList getXMLErrorList(final Node fromNode, final int count) throws Exception { + final NodeList errorList = (NodeList) ERROR_LIST.evaluate(fromNode, XPathConstants.NODESET); + assertNotNull("Root errors node is empty", errorList); + assertEquals("Root errors node child count", count, errorList.getLength()); + return errorList; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java new file mode 100644 index 0000000..30a0449 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfErrorTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; + +/** + * Unit tests for RestconfError. + * + * @author Devin Avery + * @author Thomas Pantelis + * + */ +public class RestconfErrorTest { + + static class Contains extends BaseMatcher<String> { + + private final String text; + + Contains(final String text) { + this.text = text; + } + + @Override + public void describeTo(final Description desc) { + desc.appendText("contains ").appendValue(text); + } + + @Override + public boolean matches(final Object arg) { + return arg != null && arg.toString().contains(text); + } + } + + @Test + public void testRestConfDocumentedException_NoCause() { + String expectedMessage = "Message"; + ErrorType expectedErrorType = ErrorType.RPC; + ErrorTag expectedErrorTag = ErrorTag.IN_USE; + RestconfError error = new RestconfError(expectedErrorType, expectedErrorTag, expectedMessage); + + validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag, null, (String) null, error); + } + + @Test + public void testRestConfDocumentedException_WithAppTag() { + String expectedMessage = "Message"; + ErrorType expectedErrorType = ErrorType.RPC; + ErrorTag expectedErrorTag = ErrorTag.IN_USE; + String expectedErrorAppTag = "application.tag"; + + RestconfError error = + new RestconfError(expectedErrorType, expectedErrorTag, expectedMessage, expectedErrorAppTag); + + validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag, expectedErrorAppTag, (String) null, + error); + } + + @Test + public void testRestConfDocumentedException_WithAppTagErrorInfo() { + String expectedMessage = "Message"; + ErrorType expectedErrorType = ErrorType.RPC; + ErrorTag expectedErrorTag = ErrorTag.IN_USE; + String expectedErrorAppTag = "application.tag"; + String errorInfo = "<extra><sessionid>session.id</sessionid></extra>"; + + RestconfError error = + new RestconfError(expectedErrorType, expectedErrorTag, expectedMessage, expectedErrorAppTag, errorInfo); + + validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag, expectedErrorAppTag, errorInfo, + error); + } + + @Test + public void testRestConfErrorWithRpcError() { + + // All fields set + RpcError rpcError = RpcResultBuilder.newError(ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error-message", + "mock app-tag", "mock error-info", new Exception("mock cause")); + + validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag", + "mock error-info", new RestconfError(rpcError)); + + // All fields set except 'info' - expect error-info set to 'cause' + rpcError = RpcResultBuilder.newError(ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock error-message", + "mock app-tag", null, new Exception("mock cause")); + + validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag", + new Contains("mock cause"), new RestconfError(rpcError)); + + // Some fields set - expect error-info set to ErrorSeverity + rpcError = RpcResultBuilder.newError(ErrorType.RPC, ErrorTag.ACCESS_DENIED, null, null, null, null); + + validateRestConfError(null, ErrorType.RPC, ErrorTag.ACCESS_DENIED, null, "<severity>error</severity>", + new RestconfError(rpcError)); + + // 'tag' field not mapped to ErrorTag - expect error-tag set to OPERATION_FAILED + rpcError = RpcResultBuilder.newWarning(ErrorType.TRANSPORT, new ErrorTag("not mapped"), null, null, null, null); + + validateRestConfError(null, ErrorType.TRANSPORT, new ErrorTag("not mapped"), null, + "<severity>warning</severity>", new RestconfError(rpcError)); + + // No fields set - edge case + rpcError = RpcResultBuilder.newError(ErrorType.APPLICATION, null, null, null, null, null); + + validateRestConfError(null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, + null, "<severity>error</severity>", new RestconfError(rpcError)); + } + + private static void validateRestConfError(final String expectedMessage, final ErrorType expectedErrorType, + final ErrorTag expectedErrorTag, final String expectedErrorAppTag, final String errorInfo, + final RestconfError error) { + + validateRestConfError(expectedMessage, expectedErrorType, expectedErrorTag, expectedErrorAppTag, + equalTo(errorInfo), error); + } + + private static void validateRestConfError(final String expectedMessage, final ErrorType expectedErrorType, + final ErrorTag expectedErrorTag, final String expectedErrorAppTag, final Matcher<String> errorInfoMatcher, + final RestconfError error) { + + assertEquals("getErrorMessage", expectedMessage, error.getErrorMessage()); + assertEquals("getErrorType", expectedErrorType, error.getErrorType()); + assertEquals("getErrorTag", expectedErrorTag, error.getErrorTag()); + assertEquals("getErrorAppTag", expectedErrorAppTag, error.getErrorAppTag()); + assertThat("getErrorInfo", error.getErrorInfo(), errorInfoMatcher); + error.toString(); // really just checking for NPE etc. Don't care about + // contents. + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java new file mode 100644 index 0000000..b69d105 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class RestconfImplNotificationSubscribingTest { + + private final String identifier = "data-change-event-subscription/datastore=OPERATIONAL/scope=ONE"; + + private static EffectiveModelContext schemaContext; + + @Mock + private BrokerFacade broker; + + @Mock + private UriInfo uriInfo; + + private ControllerContext controllerContext; + private RestconfImpl restconfImpl; + + @BeforeClass + public static void init() throws FileNotFoundException { + schemaContext = YangParserTestUtils.parseYangFiles(TestRestconfUtils.loadFiles("/notifications")); + } + + @AfterClass + public static void cleanUp() { + WebSocketServer.destroyInstance(); // NETCONF-604 + } + + @Before + public void setup() { + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + restconfImpl = RestconfImpl.newInstance(broker, controllerContext); + + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + Notificator.createListener(path, identifier, NotificationOutputType.XML, controllerContext); + } + + @Test + public void startTimeTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00Z")))); + Notificator.removeAllListeners(); + } + + @Test + public void milisecsTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00.12345Z")))); + Notificator.removeAllListeners(); + } + + @Test + public void zonesPlusTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00+01:00")))); + Notificator.removeAllListeners(); + } + + @Test + public void zonesMinusTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00-01:00")))); + Notificator.removeAllListeners(); + } + + @Test + public void startAndStopTimeTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00Z")), + Map.entry("stop-time", List.of("2014-10-25T12:31:00Z")))); + Notificator.removeAllListeners(); + } + + @Test(expected = RestconfDocumentedException.class) + public void stopTimeTest() { + subscribe(Set.of(Map.entry("stop-time", List.of("2014-10-25T12:31:00Z")))); + Notificator.removeAllListeners(); + } + + @Test(expected = RestconfDocumentedException.class) + public void badParamTest() { + subscribe(Set.of(Map.entry("time", List.of("2014-10-25T12:31:00Z")))); + Notificator.removeAllListeners(); + } + + @Test(expected = IllegalArgumentException.class) + public void badValueTest() { + subscribe(Set.of(Map.entry("start-time", List.of("badvalue")))); + Notificator.removeAllListeners(); + } + + @Test(expected = IllegalArgumentException.class) + public void badZonesTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00Z+1:00")))); + Notificator.removeAllListeners(); + } + + @Test(expected = IllegalArgumentException.class) + public void badMilisecsTest() { + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00:0026Z")))); + Notificator.removeAllListeners(); + } + + @Test + public void onNotifiTest() throws Exception { + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + final PathArgument pathValue = NodeIdentifier.create(QName.create("module", "2016-12-14", "localName")); + final ListenerAdapter listener = Notificator.createListener(path, identifier, NotificationOutputType.XML, + controllerContext); + + subscribe(Set.of(Map.entry("start-time", List.of("2014-10-25T10:02:00Z")))); + + Instant startOrig = listener.getStart(); + assertNotNull(startOrig); + listener.onDataTreeChanged(List.of()); + + startOrig = listener.getStart(); + assertNull(startOrig); + } + + private void subscribe(final Set<Entry<String, List<String>>> entries) { + final MultivaluedMap<String, String> map = mock(MultivaluedMap.class); + when(uriInfo.getQueryParameters()).thenReturn(map); + final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8181/" + identifier); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + when(map.entrySet()).thenReturn(entries); + restconfImpl.subscribeToStream(identifier, uriInfo); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java new file mode 100644 index 0000000..3174908 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; +import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture; + +import java.io.FileNotFoundException; +import java.net.URI; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.rest.api.Draft02; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.restconf.common.errors.RestconfError; +import org.opendaylight.yangtools.yang.common.Empty; +import org.opendaylight.yangtools.yang.common.ErrorTag; +import org.opendaylight.yangtools.yang.common.ErrorType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +/** + * See {@link InvokeRpcMethodTest}. + */ +public class RestconfImplTest { + + private static EffectiveModelContext schemaContext; + + private final BrokerFacade brokerFacade = mock(BrokerFacade.class); + private final ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + private final RestconfImpl restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs", "/modules/restconf-module-testing"); + } + + @AfterClass + public static void cleanUp() { + WebSocketServer.destroyInstance(); // NETCONF-604 + } + + @Test + public void binaryKeyTest() { + final List<Byte> al = new ArrayList<>(); + al.add((byte) 1); + binaryKeyTest(al, al); + } + + private static void binaryKeyTest(final List<Byte> al, final List<Byte> al2) { + + final QName keyDef = QName.create("test:key:binary", "2017-08-14", "b1"); + + final Map<QName, Object> uriKeyValues = new HashMap<>(); + uriKeyValues.put(keyDef, al.toArray()); + + final MapEntryNode payload = mock(MapEntryNode.class); + final NodeIdentifierWithPredicates nodeIdWithPred = + NodeIdentifierWithPredicates.of(keyDef, keyDef, al2.toArray()); + when(payload.getIdentifier()).thenReturn(nodeIdWithPred); + + final List<QName> keyDefinitions = new ArrayList<>(); + keyDefinitions.add(keyDef); + RestconfImpl.isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions); + } + + @Test + public void binaryKeyFailTest() { + final List<Byte> al = new ArrayList<>(); + al.add((byte) 1); + final List<Byte> al2 = new ArrayList<>(); + try { + binaryKeyTest(al, al2); + } catch (final RestconfDocumentedException e) { + final RestconfError err = e.getErrors().iterator().next(); + assertEquals(ErrorType.PROTOCOL, err.getErrorType()); + assertEquals(ErrorTag.INVALID_VALUE, err.getErrorTag()); + } + } + + @Test + public void testExample() throws FileNotFoundException, ParseException { + final NormalizedNode normalizedNodeData = TestUtils.prepareNormalizedNodeWithIetfInterfacesInterfacesData(); + when(brokerFacade.readOperationalData(isNull())).thenReturn(normalizedNodeData); + assertEquals(normalizedNodeData, + brokerFacade.readOperationalData(null)); + } + + @Test + public void testRpcForMountpoint() throws Exception { + final QName qname = QName.create("namespace", "2010-10-10", "localname"); + final UriInfo uriInfo = mock(UriInfo.class); + doReturn(new MultivaluedHashMap<>()).when(uriInfo).getQueryParameters(anyBoolean()); + + final NormalizedNodeContext ctx = mock(NormalizedNodeContext.class); + final RpcDefinition rpc = mock(RpcDefinition.class, + withSettings().extraInterfaces(RpcEffectiveStatement.class)); + doReturn(qname).when(rpc).getQName(); + + final InputSchemaNode input = mock(InputSchemaNode.class, + withSettings().extraInterfaces(InputEffectiveStatement.class)); + final QName inputQName = YangConstants.operationInputQName(qname.getModule()); + doReturn(input).when(rpc).getInput(); + doReturn(inputQName).when(input).getQName(); + doReturn(Optional.of(input)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(inputQName); + + final OutputSchemaNode output = mock(OutputSchemaNode.class, + withSettings().extraInterfaces(OutputEffectiveStatement.class)); + final QName outputQName = YangConstants.operationInputQName(qname.getModule()); + doReturn(output).when(rpc).getOutput(); + doReturn(outputQName).when(output).getQName(); + doReturn(Optional.of(output)).when((RpcEffectiveStatement) rpc).findSchemaTreeNode(outputQName); + + final EffectiveModelContext mountContext = mock(EffectiveModelContext.class); + final ModuleEffectiveStatement mountModule = mock(ModuleEffectiveStatement.class); + doReturn(Map.of(qname.getModule(), mountModule)).when(mountContext).getModuleStatements(); + doReturn(Optional.of(rpc)).when(mountModule).findSchemaTreeNode(qname); + + final DOMMountPoint mount = mock(DOMMountPoint.class); + doReturn(Optional.of(FixedDOMSchemaService.of(mountContext))).when(mount).getService(DOMSchemaService.class); + + doReturn(InstanceIdentifierContext.ofRpcInput(mountContext, rpc, mount)) + .when(ctx).getInstanceIdentifierContext(); + + final DOMRpcService rpcService = mock(DOMRpcService.class); + doReturn(Optional.of(rpcService)).when(mount).getService(DOMRpcService.class); + doReturn(immediateFluentFuture(mock(DOMRpcResult.class))).when(rpcService) + .invokeRpc(any(QName.class), any(NormalizedNode.class)); + restconfImpl.invokeRpc("randomId", ctx, uriInfo); + restconfImpl.invokeRpc("ietf-netconf", ctx, uriInfo); + verify(rpcService, times(2)).invokeRpc(any(QName.class), any()); + } + + /** + * Create notification stream for toaster module. + */ + @Test + public void createNotificationStreamTest() { + final QName rpcQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", + "2014-01-14", "create-notification-stream"); + + final RpcDefinition schemaNode = schemaContext.getOperations().stream() + .filter(rpc -> rpc.getQName().equals(rpcQName)) + .findFirst() + .orElseThrow(); + + final NormalizedNodeContext payload = mock(NormalizedNodeContext.class); + doReturn(InstanceIdentifierContext.ofRpcInput(schemaContext, schemaNode, null)).when(payload) + .getInstanceIdentifierContext(); + + final Set<DataContainerChild> children = new HashSet<>(); + final LeafSetNode child = mock(LeafSetNode.class); + + final LeafSetEntryNode entryNode = mock(LeafSetEntryNode.class); + when(entryNode.body()).thenReturn("(http://netconfcentral.org/ns/toaster?revision=2009-11-20)toastDone"); + when(child.body()).thenReturn(Set.of(entryNode)); + children.add(child); + + final ContainerNode normalizedNode = mock(ContainerNode.class); + doReturn(normalizedNode).when(payload).getData(); + doReturn(children).when(normalizedNode).body(); + + // register notification + final NormalizedNodeContext context = restconfImpl + .invokeRpc("sal-remote:create-notification-stream", payload, null); + assertNotNull(context); + } + + /** + * Tests stream entry node. + */ + @Test + public void toStreamEntryNodeTest() { + final Module restconfModule = controllerContext.getRestconfModule(); + final DataSchemaNode streamSchemaNode = controllerContext + .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); + final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode; + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = + SchemaAwareBuilders.mapEntryBuilder(listStreamSchemaNode); + var instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "name"); + final DataSchemaNode nameSchemaNode = instanceDataChildrenByName.get(0).child; + streamNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) nameSchemaNode) + .withValue("") + .build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "description"); + final DataSchemaNode descriptionSchemaNode = instanceDataChildrenByName.get(0).child; + streamNodeValues.withChild(SchemaAwareBuilders.leafBuilder((LeafSchemaNode) nameSchemaNode) + .withValue("DESCRIPTION_PLACEHOLDER") + .build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-support"); + final DataSchemaNode replaySupportSchemaNode = instanceDataChildrenByName.get(0).child; + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode).withValue(Boolean.TRUE).build()); + + instanceDataChildrenByName = + ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "replay-log-creation-time"); + final DataSchemaNode replayLogCreationTimeSchemaNode = instanceDataChildrenByName.get(0).child; + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode).withValue("").build()); + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(listStreamSchemaNode, "events"); + final DataSchemaNode eventsSchemaNode = instanceDataChildrenByName.get(0).child; + streamNodeValues.withChild( + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) eventsSchemaNode).withValue(Empty.value()).build()); + assertNotNull(streamNodeValues.build()); + } + + /** + * Subscribe for notification stream of toaster module. + */ + @Test + public void subscribeToNotificationStreamTest() throws Exception { + final String identifier = "create-notification-stream/toaster:toastDone"; + + // register test notification stream + Notificator.createNotificationListener( + List.of(Absolute.of(QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toastDone"))), + identifier, "XML", controllerContext); + + final UriInfo uriInfo = mock(UriInfo.class); + final UriBuilder uriBuilder = mock(UriBuilder.class); + when(uriBuilder.port(8181)).thenReturn(uriBuilder); + when(uriBuilder.replacePath(identifier)).thenReturn(uriBuilder); + when(uriBuilder.build()).thenReturn(new URI("")); + when(uriBuilder.scheme("ws")).thenReturn(uriBuilder); + when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + final MultivaluedMap<String, String> map = mock(MultivaluedMap.class); + final Set<Entry<String, List<String>>> set = new HashSet<>(); + when(map.entrySet()).thenReturn(set); + when(uriInfo.getQueryParameters()).thenReturn(map); + + // subscribe to stream and verify response + final NormalizedNodeContext response = restconfImpl.subscribeToStream(identifier, uriInfo); + + // remove test notification stream + Notificator.removeAllListeners(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java new file mode 100644 index 0000000..d96cc77 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertNotNull; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.transform.OutputKeys; +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 org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public final class TestUtils { + + private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class); + + private TestUtils() { + + } + + public static EffectiveModelContext loadSchemaContext(final String... yangPath) throws FileNotFoundException { + final List<File> files = new ArrayList<>(); + for (final String path : yangPath) { + final String pathToFile = TestUtils.class.getResource(path).getPath(); + final File testDir = new File(pathToFile); + final String[] fileList = testDir.list(); + if (fileList == null) { + throw new FileNotFoundException(pathToFile); + } + + for (final String fileName : fileList) { + final File file = new File(testDir, fileName); + if (file.isDirectory() == false) { + files.add(file); + } + } + } + + return YangParserTestUtils.parseYangFiles(files); + } + + public static Module findModule(final Collection<? extends Module> modules, final String moduleName) { + for (final Module module : modules) { + if (module.getName().equals(moduleName)) { + return module; + } + } + return null; + } + + public static Document loadDocumentFrom(final InputStream inputStream) { + try { + return UntrustedXML.newDocumentBuilder().parse(inputStream); + } catch (SAXException | IOException e) { + LOG.error("Error during loading Document from XML", e); + return null; + } + } + + public static String getDocumentInPrintableForm(final Document doc) { + try { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final TransformerFactory tf = TransformerFactory.newInstance(); + final Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + + transformer.transform(new DOMSource(requireNonNull(doc)), new StreamResult(new OutputStreamWriter(out, + StandardCharsets.UTF_8))); + final byte[] charData = out.toByteArray(); + return new String(charData, StandardCharsets.UTF_8); + } catch (final TransformerException e) { + final String msg = "Error during transformation of Document into String"; + LOG.error(msg, e); + return msg; + } + + } + + /** + * Searches module with name {@code searchedModuleName} in {@code modules}. If module name isn't specified and + * module set has only one element then this element is returned. + * + */ + public static Module resolveModule(final String searchedModuleName, final Collection<? extends Module> modules) { + assertNotNull("Modules can't be null.", modules); + if (searchedModuleName != null) { + for (final Module m : modules) { + if (m.getName().equals(searchedModuleName)) { + return m; + } + } + } else if (modules.size() == 1) { + return modules.iterator().next(); + } + return null; + } + + public static DataSchemaNode resolveDataSchemaNode(final String searchedDataSchemaName, final Module module) { + assertNotNull("Module can't be null", module); + + if (searchedDataSchemaName != null) { + for (final DataSchemaNode dsn : module.getChildNodes()) { + if (dsn.getQName().getLocalName().equals(searchedDataSchemaName)) { + return dsn; + } + } + } else if (module.getChildNodes().size() == 1) { + return module.getChildNodes().iterator().next(); + } + return null; + } + + public static QName buildQName(final String name, final String uri, final String date, final String prefix) { + return QName.create(XMLNamespace.of(uri), Revision.ofNullable(date), name); + } + + public static QName buildQName(final String name, final String uri, final String date) { + return buildQName(name, uri, date, null); + } + + public static QName buildQName(final String name) { + return buildQName(name, "", null); + } + + public static String loadTextFile(final String filePath) throws IOException { + final FileReader fileReader = new FileReader(filePath, StandardCharsets.UTF_8); + final BufferedReader bufReader = new BufferedReader(fileReader); + + String line = null; + final StringBuilder result = new StringBuilder(); + while ((line = bufReader.readLine()) != null) { + result.append(line); + } + bufReader.close(); + return result.toString(); + } + + private static Pattern patternForStringsSeparatedByWhiteChars(final String... substrings) { + final StringBuilder pattern = new StringBuilder(); + pattern.append(".*"); + for (final String substring : substrings) { + pattern.append(substring); + pattern.append("\\s*"); + } + pattern.append(".*"); + return Pattern.compile(pattern.toString(), Pattern.DOTALL); + } + + public static boolean containsStringData(final String jsonOutput, final String... substrings) { + final Pattern pattern = patternForStringsSeparatedByWhiteChars(substrings); + final Matcher matcher = pattern.matcher(jsonOutput); + return matcher.matches(); + } + + public static NodeIdentifier getNodeIdentifier(final String localName, final String namespace, + final String revision) throws ParseException { + return new NodeIdentifier(QName.create(namespace, revision, localName)); + } + + public static NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName, + final String namespace, final String revision, final Map<String, Object> keys) throws ParseException { + final Map<QName, Object> predicate = new HashMap<>(); + for (final String key : keys.keySet()) { + predicate.put(QName.create(namespace, revision, key), keys.get(key)); + } + + return NodeIdentifierWithPredicates.of(QName.create(namespace, revision, localName), predicate); + } + + public static NodeIdentifierWithPredicates getNodeIdentifierPredicate(final String localName, + final String namespace, final String revision, final String... keysAndValues) throws ParseException { + checkArgument(keysAndValues.length % 2 == 0, "number of keys argument have to be divisible by 2 (map)"); + final Map<QName, Object> predicate = new HashMap<>(); + + int index = 0; + while (index < keysAndValues.length) { + predicate.put(QName.create(namespace, revision, keysAndValues[index++]), keysAndValues[index++]); + } + + return NodeIdentifierWithPredicates.of(QName.create(namespace, revision, localName), predicate); + } + + public static NormalizedNode prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException { + final String ietfInterfacesDate = "2013-07-04"; + final String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryNode = + ImmutableMapEntryNodeBuilder.create(); + + final Map<String, Object> predicates = new HashMap<>(); + predicates.put("name", "eth0"); + + mapEntryNode.withNodeIdentifier(getNodeIdentifierPredicate("interface", namespace, ietfInterfacesDate, + predicates)); + mapEntryNode + .withChild(new ImmutableLeafNodeBuilder<String>() + .withNodeIdentifier(getNodeIdentifier("name", namespace, ietfInterfacesDate)).withValue("eth0") + .build()); + mapEntryNode.withChild(new ImmutableLeafNodeBuilder<String>() + .withNodeIdentifier(getNodeIdentifier("type", namespace, ietfInterfacesDate)) + .withValue("ethernetCsmacd").build()); + mapEntryNode.withChild(new ImmutableLeafNodeBuilder<Boolean>() + .withNodeIdentifier(getNodeIdentifier("enabled", namespace, ietfInterfacesDate)) + .withValue(Boolean.FALSE).build()); + mapEntryNode.withChild(new ImmutableLeafNodeBuilder<String>() + .withNodeIdentifier(getNodeIdentifier("description", namespace, ietfInterfacesDate)) + .withValue("some interface").build()); + + return mapEntryNode.build(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java new file mode 100644 index 0000000..1b61ea1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; +import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.CreateDataChangeEventSubscriptionInput1.Scope; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaAwareBuilders; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.InputSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class URIParametersParsing { + + private RestconfImpl restconf; + private BrokerFacade mockedBrokerFacade; + private ControllerContext controllerContext; + + @Before + public void init() throws FileNotFoundException, ReactorException { + mockedBrokerFacade = mock(BrokerFacade.class); + controllerContext = TestRestconfUtils.newControllerContext( + TestUtils.loadSchemaContext("/datastore-and-scope-specification")); + restconf = RestconfImpl.newInstance(mockedBrokerFacade, controllerContext); + } + + @Test + public void resolveURIParametersConcreteValues() { + resolveURIParameters("OPERATIONAL", "SUBTREE", LogicalDatastoreType.OPERATIONAL, Scope.SUBTREE); + } + + @Test + public void resolveURIParametersDefaultValues() { + resolveURIParameters(null, null, LogicalDatastoreType.CONFIGURATION, Scope.BASE); + } + + private void resolveURIParameters(final String datastore, final String scope, + final LogicalDatastoreType datastoreExpected, final Scope scopeExpected) { + + final InstanceIdentifierBuilder iiBuilder = YangInstanceIdentifier.builder(); + iiBuilder.node(QName.create("", "dummyStreamName")); + + final String datastoreValue = datastore == null ? "CONFIGURATION" : datastore; + final String scopeValue = scope == null ? "BASE" : scope + ""; + Notificator.createListener(iiBuilder.build(), "dummyStreamName/datastore=" + datastoreValue + "/scope=" + + scopeValue, NotificationOutputType.XML, controllerContext); + + final UriInfo mockedUriInfo = mock(UriInfo.class); + @SuppressWarnings("unchecked") + final MultivaluedMap<String, String> mockedMultivaluedMap = mock(MultivaluedMap.class); + when(mockedMultivaluedMap.getFirst(eq("datastore"))).thenReturn(datastoreValue); + when(mockedMultivaluedMap.getFirst(eq("scope"))).thenReturn(scopeValue); + + when(mockedUriInfo.getQueryParameters(eq(false))).thenReturn(mockedMultivaluedMap); + + final UriBuilder uriBuilder = UriBuilder.fromUri("www.whatever.com"); + when(mockedUriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder); + + restconf.invokeRpc("sal-remote:create-data-change-event-subscription", + prepareDomRpcNode(datastoreValue, scopeValue), mockedUriInfo); + + final ListenerAdapter listener = + Notificator.getListenerFor("data-change-event-subscription/opendaylight-inventory:nodes/datastore=" + + datastoreValue + "/scope=" + scopeValue); + assertNotNull(listener); + } + + private NormalizedNodeContext prepareDomRpcNode(final String datastore, final String scope) { + final EffectiveModelContext schema = controllerContext.getGlobalSchema(); + final Module rpcSalRemoteModule = schema.findModule("sal-remote", Revision.of("2014-01-14")).get(); + final QName rpcQName = + QName.create(rpcSalRemoteModule.getQNameModule(), "create-data-change-event-subscription"); + final RpcDefinition rpcDef = Mockito.mock(RpcDefinition.class); + ContainerLike rpcInputSchemaNode = null; + for (final RpcDefinition rpc : rpcSalRemoteModule.getRpcs()) { + if (rpcQName.isEqualWithoutRevision(rpc.getQName())) { + rpcInputSchemaNode = rpc.getInput(); + break; + } + } + assertNotNull("RPC ContainerSchemaNode was not found!", rpcInputSchemaNode); + + final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> container = + SchemaAwareBuilders.containerBuilder(rpcInputSchemaNode); + + final QName pathQName = + QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path"); + final DataSchemaNode pathSchemaNode = rpcInputSchemaNode.getDataChildByName(pathQName); + assertTrue(pathSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> pathNode = SchemaAwareBuilders.leafBuilder((LeafSchemaNode) pathSchemaNode) + .withValue(YangInstanceIdentifier.builder() + .node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build()).build(); + container.withChild(pathNode); + + final AugmentationSchemaNode augmentationSchema = requireNonNull(rpcInputSchemaNode.getAvailableAugmentations() + .iterator().next()); + final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder = + SchemaAwareBuilders.augmentationBuilder(augmentationSchema); + + final QName dataStoreQName = QName.create("urn:sal:restconf:event:subscription", "2014-07-08", "datastore"); + final DataSchemaNode dsSchemaNode = augmentationSchema.getDataChildByName(dataStoreQName); + assertTrue(dsSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> dsNode = SchemaAwareBuilders.leafBuilder((LeafSchemaNode) dsSchemaNode) + .withValue(datastore).build(); + augmentationBuilder.withChild(dsNode); + + final QName scopeQName = QName.create("urn:sal:restconf:event:subscription", "2014-07-08", "scope"); + final DataSchemaNode scopeSchemaNode = augmentationSchema.getDataChildByName(scopeQName); + assertTrue(scopeSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> scopeNode = SchemaAwareBuilders.leafBuilder((LeafSchemaNode) scopeSchemaNode) + .withValue(scope).build(); + augmentationBuilder.withChild(scopeNode); + + final QName outputQName = + QName.create("urn:sal:restconf:event:subscription", "2014-07-08", "notification-output-type"); + final DataSchemaNode outputSchemaNode = augmentationSchema.getDataChildByName(outputQName); + assertTrue(outputSchemaNode instanceof LeafSchemaNode); + final LeafNode<Object> outputNode = + SchemaAwareBuilders.leafBuilder((LeafSchemaNode) outputSchemaNode).withValue("XML").build(); + augmentationBuilder.withChild(outputNode); + + container.withChild(augmentationBuilder.build()); + + when(rpcDef.getInput()).thenReturn((InputSchemaNode) rpcInputSchemaNode); + when(rpcDef.getQName()).thenReturn(rpcQName); + + return new NormalizedNodeContext(InstanceIdentifierContext.ofRpcInput(schema, rpcDef, null), container.build()); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java new file mode 100644 index 0000000..8178674 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URITest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import java.io.FileNotFoundException; +import java.util.Optional; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.dom.api.DOMMountPoint; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.spi.FixedDOMSchemaService; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.restconf.common.context.InstanceIdentifierContext; +import org.opendaylight.restconf.common.errors.RestconfDocumentedException; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class URITest { + + private static EffectiveModelContext schemaContext; + private static EffectiveModelContext mountSchemaContext; + + private final DOMMountPoint mountInstance = mock(DOMMountPoint.class); + private final ControllerContext controllerContext = + TestRestconfUtils.newControllerContext(schemaContext, mountInstance); + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContext = TestUtils.loadSchemaContext("/full-versions/yangs"); + mountSchemaContext = TestUtils.loadSchemaContext("/test-config-data/yang2"); + } + + @Test + public void testToInstanceIdentifierList() { + InstanceIdentifierContext instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:userWithoutClass/foo"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass"); + + instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:userWithoutClass/foo"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass"); + + instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:user/foo/boo"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "user"); + + instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:user//boo"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "user"); + + } + + @Test + public void testToInstanceIdentifierWithDoubleSlash() { + InstanceIdentifierContext instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:food//nonalcoholic"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "nonalcoholic"); + + instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:userWithoutClass//"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass"); + + instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:userWithoutClass///inner-container"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "inner-container"); + } + + @Test + public void testToInstanceIdentifierListWithNullKey() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:user/null/boo")); + } + + @Test + public void testToInstanceIdentifierListWithMissingKey() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:user/foo")); + } + + @Test + public void testToInstanceIdentifierContainer() { + final InstanceIdentifierContext instanceIdentifier = + controllerContext.toInstanceIdentifier("simple-nodes:users"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "users"); + assertTrue(instanceIdentifier.getSchemaNode() instanceof ContainerSchemaNode); + assertEquals(2, ((ContainerSchemaNode) instanceIdentifier.getSchemaNode()).getChildNodes().size()); + } + + @Test + public void testToInstanceIdentifierChoice() { + final InstanceIdentifierContext instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:food/nonalcoholic"); + assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "nonalcoholic"); + } + + @Test + public void testToInstanceIdentifierChoiceException() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:food/snack")); + } + + @Test + public void testToInstanceIdentifierCaseException() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:food/sports-arena")); + } + + @Test + public void testToInstanceIdentifierChoiceCaseException() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:food/snack/sports-arena")); + } + + @Test + public void testToInstanceIdentifierWithoutNode() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes")); + } + + @Test + public void testMountPointWithExternModul() { + initSchemaService(); + final InstanceIdentifierContext instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class/student/name"); + assertEquals( + "[(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)class, " + + "(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)student, " + + "(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)student" + + "[{(urn:ietf:params:xml:ns:yang:test-interface2?revision=2014-08-01)name=name}]]", + ImmutableList.copyOf(instanceIdentifier.getInstanceIdentifier().getPathArguments()).toString()); + } + + @Test + public void testMountPointWithoutExternModul() { + initSchemaService(); + final InstanceIdentifierContext instanceIdentifier = controllerContext + .toInstanceIdentifier("simple-nodes:users/yang-ext:mount/"); + assertTrue(Iterables.isEmpty(instanceIdentifier.getInstanceIdentifier().getPathArguments())); + } + + @Test + public void testMountPointWithoutMountPointSchema() { + assertThrows(RestconfDocumentedException.class, + () -> controllerContext.toInstanceIdentifier("simple-nodes:users/yang-ext:mount/test-interface2:class")); + } + + private void initSchemaService() { + doReturn(Optional.of(FixedDOMSchemaService.of(mountSchemaContext))).when(mountInstance) + .getService(DOMSchemaService.class); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnInstanceIdentifierTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnInstanceIdentifierTest.java new file mode 100644 index 0000000..b0dd7a2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnInstanceIdentifierTest.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class XmlAndJsonToCnSnInstanceIdentifierTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + dataLoad("/instanceidentifier/yang", 4, "instance-identifier-module", "cont"); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnLeafRefTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnLeafRefTest.java new file mode 100644 index 0000000..df13bb8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlAndJsonToCnSnLeafRefTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class XmlAndJsonToCnSnLeafRefTest extends YangAndXmlAndDataSchemaLoader { + + final QName refContQName = QName.create("referenced:module", "2014-04-17", "cont"); + final QName refLf1QName = QName.create(this.refContQName, "lf1"); + final QName contQName = QName.create("leafref:module", "2014-04-17", "cont"); + final QName lf1QName = QName.create(this.contQName, "lf1"); + final QName lf2QName = QName.create(this.contQName, "lf2"); + final QName lf3QName = QName.create(this.contQName, "lf3"); + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + dataLoad("/leafref/yang", 2, "leafref-module", "cont"); + } + + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/YangAndXmlAndDataSchemaLoader.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/YangAndXmlAndDataSchemaLoader.java new file mode 100644 index 0000000..9192691 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/YangAndXmlAndDataSchemaLoader.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.FileNotFoundException; +import java.util.Collection; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; + +public abstract class YangAndXmlAndDataSchemaLoader { + protected static Collection<? extends Module> modules; + protected static DataSchemaNode dataSchemaNode; + protected static String searchedModuleName; + protected static String searchedDataSchemaName; + protected static String schemaNodePath; + + protected static void dataLoad(final String yangPath) throws FileNotFoundException { + dataLoad(yangPath, 1, null, null); + } + + protected static void dataLoad(final String yangPath, final int modulesNumber, final String moduleName, + final String dataSchemaName) throws FileNotFoundException { + modules = TestUtils.loadSchemaContext(yangPath).getModules(); + assertEquals(modulesNumber, modules.size()); + final Module module = TestUtils.resolveModule(moduleName, modules); + searchedModuleName = module == null ? "" : module.getName(); + assertNotNull(module); + dataSchemaNode = TestUtils.resolveDataSchemaNode(dataSchemaName, module); + searchedDataSchemaName = dataSchemaNode == null ? "" : dataSchemaNode.getQName().getLocalName(); + assertNotNull(dataSchemaNode); + schemaNodePath = searchedModuleName + ":" + searchedDataSchemaName; + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModule.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModule.java new file mode 100644 index 0000000..cabdc6c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModule.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test.incubate; + +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import javax.annotation.PreDestroy; +import javax.inject.Singleton; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractBaseDataBrokerTest; +import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest; +import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMMountPointService; +import org.opendaylight.mdsal.dom.api.DOMNotificationPublishService; +import org.opendaylight.mdsal.dom.api.DOMNotificationService; +import org.opendaylight.mdsal.dom.api.DOMRpcService; +import org.opendaylight.mdsal.dom.api.DOMSchemaService; +import org.opendaylight.mdsal.dom.broker.DOMMountPointServiceImpl; +import org.opendaylight.mdsal.dom.broker.DOMNotificationRouter; +import org.opendaylight.mdsal.dom.broker.DOMRpcRouter; +import org.opendaylight.mdsal.dom.spi.DOMNotificationSubscriptionListenerRegistry; +import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider; + +/** + * Guice Module which binds the mdsal (not controller) {@link DataBroker} & Co. + * in-memory implementation suitable for tests. + * + * <p>This class is here only temporarily and it can and should be removed and + * replaced when the equivalent will be offered by the mdsal project itself; see + * <a href="https://jira.opendaylight.org/browse/MDSAL-418">MDSAL-418</a>. It is + * also copy/pasted to org.opendaylight.restconf.nb.rfc8040.test.incubate.InMemoryMdsalModule. + * + * <p>BEWARE: Do *NOT* use this module in component tests or applications mixing + * code requiring the old controller and the new mdsal {@link DataBroker} & Co. + * APIs together - because this binds a *SEPARATE* {@link InMemoryDOMDataStore}, + * and doesn't delegate to controller's InMemoryDOMDataStore. This is just fine + * for tests where all code under test already uses only the mdsal APIs. + * + * @author Michael Vorburger.ch + */ +public class InMemoryMdsalModule extends AbstractModule { + + private static final int NOTIFICATION_SERVICE_QUEUE_DEPTH = 128; + + private final AbstractBaseDataBrokerTest dataBrokerTest; + private final DOMNotificationRouter domNotificationRouter; + + public InMemoryMdsalModule() throws Exception { + dataBrokerTest = new AbstractConcurrentDataBrokerTest(true) { // NOT AbstractDataBrokerTest + }; + dataBrokerTest.setup(); + + domNotificationRouter = DOMNotificationRouter.create(NOTIFICATION_SERVICE_QUEUE_DEPTH); + } + + @Override + protected void configure() { + } + + @Provides + @Singleton + DataBroker getDataBroker() { + return dataBrokerTest.getDataBroker(); + } + + @Provides + @Singleton DOMDataBroker getDOMDataBroker() { + return dataBrokerTest.getDomBroker(); + } + + @Provides + @Singleton DOMNotificationRouter getDOMNotificationRouter() { + return dataBrokerTest.getDataBrokerTestCustomizer().getDomNotificationRouter(); + } + + @Provides + @Singleton DOMSchemaService getSchemaService() { + return dataBrokerTest.getDataBrokerTestCustomizer().getSchemaService(); + } + + @Provides + @Singleton EffectiveModelContextProvider getSchemaContextProvider() { + DOMSchemaService schemaService = dataBrokerTest.getDataBrokerTestCustomizer().getSchemaService(); + if (schemaService instanceof EffectiveModelContextProvider) { + return (EffectiveModelContextProvider) schemaService; + } + throw new IllegalStateException( + "The schema service isn't a SchemaContextProvider, it's a " + schemaService.getClass()); + } + + @Provides + @Singleton DOMMountPointService getDOMMountPoint() { + return new DOMMountPointServiceImpl(); + } + + @Provides + @Singleton DOMNotificationService getDOMNotificationService() { + return domNotificationRouter; + } + + @Provides + @Singleton DOMNotificationPublishService getDOMNotificationPublishService() { + return domNotificationRouter; + } + + @Provides + @Singleton DOMNotificationSubscriptionListenerRegistry getDOMNotificationSubscriptionListenerRegistry() { + return domNotificationRouter; + } + + @Provides + @Singleton DOMRpcService getDOMRpcService(DOMSchemaService schemaService) { + return DOMRpcRouter.newInstance(schemaService).getRpcService(); + } + + @PreDestroy + public void close() { + // TODO When moving this to mdsal, must close components to shut down Threads etc. + // but cannot do this here (in netconf) yet, because we need to change AbstractBaseDataBrokerTest & Co.. + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModuleTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModuleTest.java new file mode 100644 index 0000000..6e01ecd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/incubate/InMemoryMdsalModuleTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.test.incubate; + +import java.util.concurrent.ExecutionException; +import javax.inject.Inject; +import org.junit.Rule; +import org.junit.Test; +import org.opendaylight.infrautils.inject.guice.testutils.AnnotationsModule; +import org.opendaylight.infrautils.inject.guice.testutils.GuiceRule; +import org.opendaylight.mdsal.binding.api.DataBroker; + +/** + * Test for {@link InMemoryMdsalModule}. + * + * <p>This will be removed when the local {@link InMemoryMdsalModule} incubating here + * in netconf will be replaced by the one from mdsal. + * + * @author Michael Vorburger.ch + */ +public class InMemoryMdsalModuleTest { + + public @Rule GuiceRule guice = new GuiceRule(InMemoryMdsalModule.class, AnnotationsModule.class); + + @Inject DataBroker dataBroker; + + @Test public void testDataBroker() throws InterruptedException, ExecutionException { + dataBroker.newReadWriteTransaction().commit().get(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/IClientMessageCallback.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/IClientMessageCallback.java new file mode 100644 index 0000000..a5323fb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/IClientMessageCallback.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.restconf.impl.websockets.client; + +/** + * Created by mbobak on 1/22/14. + */ +public interface IClientMessageCallback { + + void onMessageReceived(Object message); +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClient.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClient.java new file mode 100644 index 0000000..a67a491 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClient.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.websockets.client; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; +import io.netty.handler.codec.http.websocketx.WebSocketVersion; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebSocketClient { + + private static final Logger LOG = LoggerFactory.getLogger(WebSocketClient.class); + + private final URI uri; + private final Bootstrap bootstrap = new Bootstrap(); + private final WebSocketClientHandler clientHandler; + private Channel clientChannel; + private final EventLoopGroup group = new NioEventLoopGroup(); + + public WebSocketClient(final URI uri, final IClientMessageCallback clientMessageCallback) { + this.uri = uri; + clientHandler = new WebSocketClientHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, + WebSocketVersion.V13, null, false, null), clientMessageCallback); + // last null could be replaced with DefaultHttpHeaders + initialize(); + } + + private void initialize() { + + String protocol = uri.getScheme(); + if (!"ws".equals(protocol)) { + throw new IllegalArgumentException("Unsupported protocol: " + protocol); + } + + bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() { + @Override + public void initChannel(final SocketChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("http-codec", new HttpClientCodec()); + pipeline.addLast("aggregator", new HttpObjectAggregator(8192)); + pipeline.addLast("ws-handler", clientHandler); + } + }); + } + + public void connect() throws InterruptedException { + LOG.info("WebSocket Client connecting"); + clientChannel = bootstrap.connect(uri.getHost(), uri.getPort()).sync().channel(); + clientHandler.handshakeFuture().sync(); + } + + public void writeAndFlush(final String message) { + clientChannel.writeAndFlush(new TextWebSocketFrame(message)); + } + + public void writeAndFlush(final Object message) { + clientChannel.writeAndFlush(message); + } + + public void ping() { + clientChannel.writeAndFlush(new PingWebSocketFrame(Unpooled.copiedBuffer(new byte[] { 1, 2, 3, 4, 5, 6 }))); + } + + public void close(final String reasonText) throws InterruptedException { + CloseWebSocketFrame closeWebSocketFrame = new CloseWebSocketFrame(1000, reasonText); + clientChannel.writeAndFlush(closeWebSocketFrame); + + // WebSocketClientHandler will close the connection when the server + // responds to the CloseWebSocketFrame. + clientChannel.closeFuture().sync(); + group.shutdownGracefully(); + } + + public static void main(final String[] args) throws Exception { + URI uri; + if (args.length > 0) { + uri = new URI(args[0]); + } else { + uri = new URI("http://192.168.1.101:8181/opendaylight-inventory:nodes"); + } + IClientMessageCallback messageCallback = new ClientMessageCallback(); + WebSocketClient webSocketClient = new WebSocketClient(uri, messageCallback); + webSocketClient.connect(); + + while (true) { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + String input = br.readLine(); + if (input.equals("q")) { + LOG.info("Would you like to close stream? (Y = yes, empty = yes)\n"); + input = br.readLine(); + if (input.equals("yes") || input.isEmpty()) { + webSocketClient.close("opendaylight-inventory:nodes"); + break; + } + } + } + } + + private static class ClientMessageCallback implements IClientMessageCallback { + @Override + public void onMessageReceived(final Object message) { + if (message instanceof TextWebSocketFrame) { + LOG.info("received message {}" + ((TextWebSocketFrame) message).text()); + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClientHandler.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClientHandler.java new file mode 100644 index 0000000..19dbbc3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/client/WebSocketClientHandler.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.websockets.client; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; +import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> { + + private static final Logger LOG = LoggerFactory.getLogger(WebSocketClientHandler.class.toString()); + private final WebSocketClientHandshaker handshaker; + private ChannelPromise handshakeFuture; + private final IClientMessageCallback messageListener; + + public WebSocketClientHandler(final WebSocketClientHandshaker handshaker, final IClientMessageCallback listener) { + this.handshaker = handshaker; + this.messageListener = listener; + } + + public ChannelFuture handshakeFuture() { + return handshakeFuture; + } + + @Override + public void handlerAdded(final ChannelHandlerContext ctx) throws Exception { + handshakeFuture = ctx.newPromise(); + } + + @Override + public void channelActive(final ChannelHandlerContext ctx) throws Exception { + handshaker.handshake(ctx.channel()); + } + + @Override + public void channelInactive(final ChannelHandlerContext ctx) throws Exception { + LOG.info("WebSocket Client disconnected!"); + } + + @Override + public void channelRead0(final ChannelHandlerContext ctx, final Object msg) throws Exception { + Channel ch = ctx.channel(); + if (!handshaker.isHandshakeComplete()) { + handshaker.finishHandshake(ch, (FullHttpResponse) msg); + LOG.info("WebSocket Client connected!"); + handshakeFuture.setSuccess(); + return; + } + + if (msg instanceof FullHttpResponse) { + FullHttpResponse response = (FullHttpResponse) msg; + throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + + response.content().toString(CharsetUtil.UTF_8) + ')'); + } + + messageListener.onMessageReceived(msg); + WebSocketFrame frame = (WebSocketFrame) msg; + + if (frame instanceof PongWebSocketFrame) { + LOG.info("WebSocket Client received pong"); + } else if (frame instanceof CloseWebSocketFrame) { + LOG.info("WebSocket Client received closing"); + ch.close(); + } + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { + LOG.info("Cause: {} .", cause.toString()); + + if (!handshakeFuture.isDone()) { + handshakeFuture.setFailure(cause); + } + + ctx.close(); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/test/RestStreamTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/test/RestStreamTest.java new file mode 100644 index 0000000..0163ce0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/websockets/test/RestStreamTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.websockets.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; +import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter; +import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper; +import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader; +import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +public class RestStreamTest extends JerseyTest { + + private static EffectiveModelContext schemaContextYangsIetf; + + private BrokerFacade brokerFacade; + private RestconfImpl restconfImpl; + + @BeforeClass + public static void init() throws FileNotFoundException, ReactorException { + schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs"); + } + + @Override + protected Application configure() { + /* enable/disable Jersey logs to console */ + // enable(TestProperties.LOG_TRAFFIC); + // enable(TestProperties.DUMP_ENTITY); + // enable(TestProperties.RECORD_LOG_LEVEL); + // set(TestProperties.RECORD_LOG_LEVEL, Level.ALL.intValue()); + + final ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContextYangsIetf); + brokerFacade = mock(BrokerFacade.class); + restconfImpl = RestconfImpl.newInstance(brokerFacade, controllerContext); + + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig = resourceConfig.registerInstances(restconfImpl, new NormalizedNodeJsonBodyWriter(), + new NormalizedNodeXmlBodyWriter(), new XmlNormalizedNodeBodyReader(controllerContext), + new JsonNormalizedNodeBodyReader(controllerContext), + new RestconfDocumentedExceptionMapper(controllerContext)); + return resourceConfig; + } + + @Test + @Ignore // Sporadic failures where jersey does not correctly pass post data to XmlNormalizedNodeBodyReader.readFrom + public void testCallRpcCallGet() throws UnsupportedEncodingException, InterruptedException { + createAndSubscribe(null); + } + + @Test + @Ignore // Sporadic failures where jersey does not correctly pass post data to XmlNormalizedNodeBodyReader.readFrom + public void testCallRpcCallGetLeaves() throws UnsupportedEncodingException, InterruptedException { + createAndSubscribe("odl-leaf-nodes-only", "true"); + } + + private void createAndSubscribe(final String queryParamName, final Object... values) + throws UnsupportedEncodingException, InterruptedException { + String uri = "/operations/sal-remote:create-data-change-event-subscription"; + String rpcInput = getRpcInput(); + final Response responseWithStreamName = post(uri, MediaType.APPLICATION_XML, rpcInput); + final Document xmlResponse = responseWithStreamName.readEntity(Document.class); + assertNotNull(xmlResponse); + final Element outputElement = xmlResponse.getDocumentElement(); + assertEquals("output",outputElement.getLocalName()); + + final Node streamNameElement = outputElement.getFirstChild(); + assertEquals("stream-name",streamNameElement.getLocalName()); + assertEquals("data-change-event-subscription/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0/" + + "datastore=CONFIGURATION/scope=BASE",streamNameElement.getTextContent()); + + uri = "/streams/stream/data-change-event-subscription/ietf-interfaces:interfaces/ietf-interfaces:interface/" + + "eth0/datastore=CONFIGURATION/scope=BASE"; + final Response responseWithRedirectionUri = get(uri, MediaType.APPLICATION_XML, null); + final URI websocketServerUri = responseWithRedirectionUri.getLocation(); + assertNotNull(websocketServerUri); + assertTrue(websocketServerUri.toString().matches(".*ws://localhost:[\\d]+/data-change-event-subscription/" + + "ietf-interfaces:interfaces/ietf-interfaces:interface/eth0.*")); + } + + private Response post(final String uri, final String mediaType, final String data) { + return target(uri).request(mediaType).post(Entity.entity(data, mediaType)); + } + + private Response get(final String uri, final String mediaType, final String queryParam, final Object... values) { + if (queryParam != null) { + return target(uri).queryParam(queryParam, values).request(mediaType).get(); + } else { + return target(uri).request(mediaType).get(); + } + } + + private static String getRpcInput() { + final StringBuilder sb = new StringBuilder(); + sb.append("<input xmlns=\"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote\">"); + sb.append("<path xmlns:int=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">/" + + "int:interfaces/int:interface[int:name='eth0']</path>"); + sb.append("</input>"); + return sb.toString(); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlAugmentedElementToCnSnTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlAugmentedElementToCnSnTest.java new file mode 100644 index 0000000..2a6f7de --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlAugmentedElementToCnSnTest.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.xml.to.cnsn.test; + + +public class XmlAugmentedElementToCnSnTest { + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java new file mode 100644 index 0000000..1cac018 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/xml/to/cnsn/test/XmlToCnSnTest.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.restconf.impl.xml.to.cnsn.test; + +import java.io.FileNotFoundException; +import org.junit.BeforeClass; +import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; + +public class XmlToCnSnTest extends YangAndXmlAndDataSchemaLoader { + + @BeforeClass + public static void initialize() throws FileNotFoundException, ReactorException { + dataLoad("/xml-to-cnsn/leafref"); + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java new file mode 100644 index 0000000..58debcb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.rest.impl; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class DepthAwareNormalizedNodeWriterTest { + + @Mock + private NormalizedNodeStreamWriter writer; + @Mock + private ContainerNode containerNodeData; + @Mock + private MapNode mapNodeData; + @Mock + private MapEntryNode mapEntryNodeData; + @Mock + private LeafSetNode<String> leafSetNodeData; + @Mock + private LeafSetEntryNode<String> leafSetEntryNodeData; + @Mock + private LeafNode<String> keyLeafNodeData; + @Mock + private LeafNode<String> anotherLeafNodeData; + + private NodeIdentifier containerNodeIdentifier; + private NodeIdentifier mapNodeIdentifier; + private NodeIdentifierWithPredicates mapEntryNodeIdentifier; + private NodeIdentifier leafSetNodeIdentifier; + private NodeWithValue<String> leafSetEntryNodeIdentifier; + private NodeIdentifier keyLeafNodeIdentifier; + private NodeIdentifier anotherLeafNodeIdentifier; + + private Collection<DataContainerChild> containerNodeValue; + private Collection<MapEntryNode> mapNodeValue; + private Collection<DataContainerChild> mapEntryNodeValue; + private Collection<LeafSetEntryNode<String>> leafSetNodeValue; + private String leafSetEntryNodeValue; + private String keyLeafNodeValue; + private String anotherLeafNodeValue; + + @Before + public void setUp() { + // identifiers + containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container")); + when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier); + + mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list")); + when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier); + + final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry"); + leafSetEntryNodeValue = "leaf-set-value"; + leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue); + when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier); + + leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set")); + when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier); + + final QName mapEntryNodeKey = QName.create("namespace", "key-field"); + keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey); + keyLeafNodeValue = "key-value"; + + mapEntryNodeIdentifier = NodeIdentifierWithPredicates.of( + QName.create("namespace", "list-entry"), mapEntryNodeKey, keyLeafNodeValue); + when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier); + when(mapEntryNodeData.findChildByArg(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData)); + + when(keyLeafNodeData.body()).thenReturn(keyLeafNodeValue); + when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier); + + anotherLeafNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "another-field")); + anotherLeafNodeValue = "another-value"; + + when(anotherLeafNodeData.body()).thenReturn(anotherLeafNodeValue); + when(anotherLeafNodeData.getIdentifier()).thenReturn(anotherLeafNodeIdentifier); + + // values + when(leafSetEntryNodeData.body()).thenReturn(leafSetEntryNodeValue); + + leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData); + when(leafSetNodeData.body()).thenReturn(leafSetNodeValue); + + containerNodeValue = Collections.singleton(leafSetNodeData); + when(containerNodeData.body()).thenReturn(containerNodeValue); + + mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData, anotherLeafNodeData); + when(mapEntryNodeData.body()).thenReturn(mapEntryNodeValue); + + mapNodeValue = Collections.singleton(mapEntryNodeData); + when(mapNodeData.body()).thenReturn(mapNodeValue); + } + + /** + * Test write {@link ContainerNode} with children but write data only to depth 1 (children will not be written). + */ + @Test + public void writeContainerWithoutChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1); + + depthWriter.write(containerNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size()); + inOrder.verify(writer, times(1)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write {@link ContainerNode} with children and write also all its children. + */ + @Test + public void writeContainerWithChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, Integer.MAX_VALUE); + + depthWriter.write(containerNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafSetEntryNode(leafSetEntryNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(leafSetEntryNodeValue); + inOrder.verify(writer, times(3)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link MapNode} with children but write data only to depth 1 (children will not be written). + */ + @Test + public void writeMapNodeWithoutChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1); + + depthWriter.write(mapNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size()); + inOrder.verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(3)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write {@link MapNode} with children and write also all its children. + * FIXME + * Although ordered writer is used leaves are not written in expected order. + * + */ + @Ignore + @Test + public void writeMapNodeWithChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, Integer.MAX_VALUE); + + depthWriter.write(mapNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size()); + inOrder.verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(1)).endNode(); + inOrder.verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(1)).endNode(); + + // FIXME this assertion is not working because leaves are not written in expected order + inOrder.verify(writer, times(1)).startLeafNode(anotherLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(anotherLeafNodeValue); + inOrder.verify(writer, times(3)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link LeafSetNode} with depth 1 (children will not be written). + */ + @Test + public void writeLeafSetNodeWithoutChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, 1); + + depthWriter.write(leafSetNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size()); + inOrder.verify(writer, times(1)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link LeafSetNode} when all its children will be written. + */ + @Test + public void writeLeafSetNodeWithChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, Integer.MAX_VALUE); + + depthWriter.write(leafSetNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafSetEntryNode(leafSetEntryNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(leafSetEntryNodeValue); + inOrder.verify(writer, times(2)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link LeafSetEntryNode}. + */ + @Test + public void writeLeafSetEntryNodeTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, Integer.MAX_VALUE); + + depthWriter.write(leafSetEntryNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startLeafSetEntryNode(leafSetEntryNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(leafSetEntryNodeValue); + inOrder.verify(writer, times(1)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link MapEntryNode} unordered to depth 1 to write only keys. + */ + @Test + public void writeMapEntryNodeUnorderedOnlyKeysTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, false, 1); + + depthWriter.write(mapEntryNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + // write only the key + inOrder.verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(2)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link MapEntryNode} unordered with full depth. + */ + @Test + public void writeMapEntryNodeUnorderedTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, false, Integer.MAX_VALUE); + + depthWriter.write(mapEntryNodeData); + + // unordered + verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + verify(writer, times(1)).scalarValue(keyLeafNodeValue); + verify(writer, times(1)).startLeafNode(anotherLeafNodeIdentifier); + verify(writer, times(1)).scalarValue(anotherLeafNodeValue); + verify(writer, times(3)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link MapEntryNode} ordered with depth 1 (children will not be written). + */ + @Test + public void writeMapEntryNodeOrderedWithoutChildrenTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, true, 1); + + depthWriter.write(mapEntryNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + inOrder.verify(writer, times(1)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(1)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(2)).endNode(); + verifyNoMoreInteractions(writer); + } + + /** + * Test write with {@link MapEntryNode} ordered and write also all its children. + * FIXME + * Although ordered writer is used leaves are not written in expected order. + * + */ + @Ignore + @Test + public void writeMapEntryNodeOrderedTest() throws Exception { + final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter( + writer, true, Integer.MAX_VALUE); + + depthWriter.write(mapEntryNodeData); + + final InOrder inOrder = inOrder(writer); + inOrder.verify(writer, times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size()); + inOrder.verify(writer, times(2)).startLeafNode(keyLeafNodeIdentifier); + inOrder.verify(writer, times(2)).scalarValue(keyLeafNodeValue); + inOrder.verify(writer, times(1)).endNode(); + // FIXME this assertion is not working because leaves are not written in expected order + inOrder.verify(writer, times(1)).startLeafNode(anotherLeafNodeIdentifier); + inOrder.verify(writer, times(2)).scalarValue(anotherLeafNodeValue); + inOrder.verify(writer, times(2)).endNode(); + verifyNoMoreInteractions(writer); + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/restconf/impl/InstanceIdentifierCodecImplTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/restconf/impl/InstanceIdentifierCodecImplTest.java new file mode 100644 index 0000000..e0d48bb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/restconf/impl/InstanceIdentifierCodecImplTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.restconf.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.FileNotFoundException; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.netconf.sal.restconf.impl.RestCodec.InstanceIdentifierCodecImpl; +import org.opendaylight.restconf.common.util.IdentityValuesDTO; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +public class InstanceIdentifierCodecImplTest { + private static EffectiveModelContext schemaContext; + + private InstanceIdentifierCodecImpl instanceIdentifierDTO; + private YangInstanceIdentifier instanceIdentifierBadNamespace; + private YangInstanceIdentifier instanceIdentifierOKList; + private YangInstanceIdentifier instanceIdentifierOKLeafList; + + @BeforeClass + public static void init() throws FileNotFoundException { + schemaContext = YangParserTestUtils.parseYangFiles( + TestRestconfUtils.loadFiles("/restconf/parser/deserializer")); + } + + @Before + public void setUp() throws Exception { + ControllerContext controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + + this.instanceIdentifierDTO = new InstanceIdentifierCodecImpl(null, controllerContext); + + final QName baseQName = QName.create("deserializer:test", "2016-06-06", "deserializer-test"); + final QName contA = QName.create(baseQName, "contA"); + final QName leafList = QName.create(baseQName, "leaf-list-A"); + + this.instanceIdentifierOKLeafList = YangInstanceIdentifier.builder() + .node(contA) + .node(new YangInstanceIdentifier.NodeWithValue<>(leafList, "instance")) + .build(); + + this.instanceIdentifierOKList = YangInstanceIdentifier.builder() + .node(NodeIdentifierWithPredicates.of( + QName.create(baseQName, "list-one-key"), + QName.create(QName.create(baseQName, "list-one-key"), "name"), "value")) + .build(); + + this.instanceIdentifierBadNamespace = YangInstanceIdentifier.builder() + .nodeWithKey(QName.create("nonexistent:module", "2016-10-17", "nonexistent-1"), + QName.create("nonexistent:module", "2016-10-17", "nonexistent"), + "value") + .build(); + } + + @Test + public void testSerializeDeserializeList() throws Exception { + final IdentityValuesDTO valuesDTO = + this.instanceIdentifierDTO.serialize(this.instanceIdentifierOKList); + + final YangInstanceIdentifier deserializedIdentifier = + this.instanceIdentifierDTO.deserialize(valuesDTO); + assertEquals(this.instanceIdentifierOKList, deserializedIdentifier); + } + + @Test + public void testSerializeDeserializeLeafList() throws Exception { + final IdentityValuesDTO valuesDTO = + this.instanceIdentifierDTO.serialize(this.instanceIdentifierOKLeafList); + + final YangInstanceIdentifier deserializedIdentifier = + this.instanceIdentifierDTO.deserialize(valuesDTO); + assertEquals(this.instanceIdentifierOKLeafList, deserializedIdentifier); + } + + @Test + public void testSerializeDeserializeBadModuleNamespace() throws Exception { + final IdentityValuesDTO valuesDTO = + this.instanceIdentifierDTO.serialize(this.instanceIdentifierBadNamespace); + assertEquals("nonexistent-1", valuesDTO.getValuesWithNamespaces().get(0).getValue()); + assertEquals("nonexistent:module", valuesDTO.getValuesWithNamespaces().get(0).getNamespace()); + + final YangInstanceIdentifier deserializedIdentifier = + this.instanceIdentifierDTO.deserialize(valuesDTO); + assertNull(deserializedIdentifier); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapterTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapterTest.java new file mode 100644 index 0000000..453f077 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapterTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import static java.time.Instant.EPOCH; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; +import org.json.JSONObject; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.WriteTransaction; +import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.dom.api.DOMDataBroker; +import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont; +import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1; +import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Builder; +import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.skyscreamer.jsonassert.JSONAssert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest { + private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapterTest.class); + + private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json"; + private static final String JSON_NOTIF_LEAVES_UPDATE = "/listener-adapter-test/notif-leaves-update.json"; + private static final String JSON_NOTIF_LEAVES_DEL = "/listener-adapter-test/notif-leaves-del.json"; + private static final String JSON_NOTIF_CREATE = "/listener-adapter-test/notif-create.json"; + private static final String JSON_NOTIF_UPDATE = "/listener-adapter-test/notif-update.json"; + private static final String JSON_NOTIF_DEL = "/listener-adapter-test/notif-del.json"; + private static final String JSON_NOTIF_WITHOUT_DATA_CREATE = + "/listener-adapter-test/notif-without-data-create.json"; + private static final String JSON_NOTIF_WITHOUT_DATA_UPDATE = + "/listener-adapter-test/notif-without-data-update.json"; + private static final String JSON_NOTIF_WITHOUT_DATA_DELETE = + "/listener-adapter-test/notif-without-data-del.json"; + + + private static final YangInstanceIdentifier PATCH_CONT_YIID = + YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(PatchCont.QNAME)); + + private static EffectiveModelContext schemaContext; + + private DataBroker dataBroker; + private DOMDataBroker domDataBroker; + private ControllerContext controllerContext; + + @BeforeClass + public static void init() { + schemaContext = YangParserTestUtils.parseYangResource( + "/instanceidentifier/yang/instance-identifier-patch-module.yang"); + } + + @Before + public void setUp() throws Exception { + dataBroker = getDataBroker(); + domDataBroker = getDomBroker(); + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + } + + class ListenerAdapterTester extends ListenerAdapter { + + private String lastNotification = null; + + ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName, + final NotificationOutputTypeGrouping.NotificationOutputType outputType, + final boolean leafNodesOnly, final boolean skipNotificationData) { + super(path, streamName, outputType, controllerContext); + setQueryParams(EPOCH, Optional.empty(), Optional.empty(), leafNodesOnly, skipNotificationData); + } + + @Override + protected void post(final Event event) { + this.lastNotification = event.getData(); + } + + public void assertGot(final String json) throws Exception { + long start = System.currentTimeMillis(); + while (true) { + if (lastNotification != null) { + break; + } + if (System.currentTimeMillis() - start > 1000) { + throw new Exception("TIMED OUT waiting for notification with " + json); + } + Thread.currentThread(); + Thread.sleep(200); + } + LOG.debug("Comparing {} {}", json, lastNotification); + JSONAssert.assertEquals(json, withFakeDate(lastNotification), false); + this.lastNotification = null; + } + } + + static String withFakeDate(final String in) { + JSONObject doc = new JSONObject(in); + JSONObject notification = doc.getJSONObject("notification"); + if (notification == null) { + return in; + } + notification.put("eventTime", "someDate"); + return doc.toString(); + } + + private String getNotifJson(final String path) throws IOException, URISyntaxException { + URL url = getClass().getResource(path); + byte[] bytes = Files.readAllBytes(Paths.get(url.toURI())); + return withFakeDate(new String(bytes, StandardCharsets.UTF_8)); + } + + @Test + public void testJsonNotifsLeaves() throws Exception { + ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", + NotificationOutputTypeGrouping.NotificationOutputType.JSON, true, false); + DOMDataTreeChangeService changeService = domDataBroker.getExtensions() + .getInstance(DOMDataTreeChangeService.class); + DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID); + changeService.registerDataTreeChangeListener(root, adapter); + + WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea"); + InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class) + .child(MyList1.class, new MyList1Key("Althea")); + writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha"); + writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DEL)); + } + + @Test + public void testJsonNotifs() throws Exception { + ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", + NotificationOutputTypeGrouping.NotificationOutputType.JSON, false, false); + DOMDataTreeChangeService changeService = domDataBroker.getExtensions() + .getInstance(DOMDataTreeChangeService.class); + DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID); + changeService.registerDataTreeChangeListener(root, adapter); + + WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea"); + InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class) + .child(MyList1.class, new MyList1Key("Althea")); + writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_CREATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha"); + writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_UPDATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_DEL)); + } + + @Test + public void testJsonNotifsWithoutData() throws Exception { + ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", + NotificationOutputTypeGrouping.NotificationOutputType.JSON, false, true); + DOMDataTreeChangeService changeService = domDataBroker.getExtensions() + .getInstance(DOMDataTreeChangeService.class); + DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID); + changeService.registerDataTreeChangeListener(root, adapter); + + WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea"); + InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class) + .child(MyList1.class, new MyList1Key("Althea")); + writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_CREATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha"); + writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build()); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_UPDATE)); + + writeTransaction = dataBroker.newWriteOnlyTransaction(); + writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid); + writeTransaction.commit(); + adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_DELETE)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerTest.java new file mode 100644 index 0000000..732c327 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.netconf.sal.streams.listeners; + +import static java.util.Objects.requireNonNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; +import org.opendaylight.mdsal.dom.api.DOMNotification; +import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.XMLNamespace; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class NotificationListenerTest { + private static final QNameModule MODULE = QNameModule.create(XMLNamespace.of("notifi:mod"), + Revision.of("2016-11-23")); + + private static EffectiveModelContext schemaContext; + + private ControllerContext controllerContext; + + @BeforeClass + public static void staticInit() throws FileNotFoundException { + schemaContext = TestUtils.loadSchemaContext("/notifications"); + } + + @Before + public void init() { + controllerContext = TestRestconfUtils.newControllerContext(schemaContext); + } + + @Test + public void notifi_leafTest() { + final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-leaf")); + + final DOMNotification notificationData = mock(DOMNotification.class); + + final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf")); + final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf); + + when(notificationData.getType()).thenReturn(schemaPathNotifi); + when(notificationData.getBody()).thenReturn(notifiBody); + + final String result = prepareJson(notificationData, schemaPathNotifi); + + assertTrue(result.contains("ietf-restconf:notification")); + assertTrue(result.contains("event-time")); + assertTrue(result.contains("notifi-module:notifi-leaf")); + assertTrue(result.contains("lf" + '"' + ":" + '"' + "value")); + } + + @Test + public void notifi_cont_leafTest() { + final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-cont")); + + final DOMNotification notificationData = mock(DOMNotification.class); + + final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf")); + final ContainerNode cont = mockCont(QName.create(MODULE, "cont"), leaf); + final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), cont); + + when(notificationData.getType()).thenReturn(schemaPathNotifi); + when(notificationData.getBody()).thenReturn(notifiBody); + + final String result = prepareJson(notificationData, schemaPathNotifi); + + assertTrue(result.contains("ietf-restconf:notification")); + assertTrue(result.contains("event-time")); + assertTrue(result.contains("notifi-module:notifi-cont")); + assertTrue(result.contains("cont")); + assertTrue(result.contains("lf" + '"' + ":" + '"' + "value")); + } + + @Test + public void notifi_list_Test() { + final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-list")); + + final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), ImmutableNodes.mapNodeBuilder() + .withNodeIdentifier(NodeIdentifier.create(QName.create(MODULE, "lst"))) + .withChild(mockMapEntry(QName.create(MODULE, "lst"), mockLeaf(QName.create(MODULE, "lf")))) + .build()); + + final DOMNotification notificationData = mock(DOMNotification.class); + when(notificationData.getType()).thenReturn(schemaPathNotifi); + when(notificationData.getBody()).thenReturn(notifiBody); + + final String result = prepareJson(notificationData, schemaPathNotifi); + + assertTrue(result.contains("ietf-restconf:notification")); + assertTrue(result.contains("event-time")); + assertTrue(result.contains("notifi-module:notifi-list")); + assertTrue(result.contains("lst")); + assertTrue(result.contains("lf" + '"' + ":" + '"' + "value")); + } + + @Test + public void notifi_grpTest() { + final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-grp")); + + final DOMNotification notificationData = mock(DOMNotification.class); + + final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf")); + final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), leaf); + + when(notificationData.getType()).thenReturn(schemaPathNotifi); + when(notificationData.getBody()).thenReturn(notifiBody); + + final String result = prepareJson(notificationData, schemaPathNotifi); + + assertTrue(result.contains("ietf-restconf:notification")); + assertTrue(result.contains("event-time")); + assertTrue(result.contains("lf" + '"' + ":" + '"' + "value")); + } + + @Test + public void notifi_augmTest() { + final Absolute schemaPathNotifi = Absolute.of(QName.create(MODULE, "notifi-augm")); + + final DOMNotification notificationData = mock(DOMNotification.class); + + final LeafNode<String> leaf = mockLeaf(QName.create(MODULE, "lf-augm")); + final AugmentationNode augm = mockAugm(leaf); + final ContainerNode notifiBody = mockCont(schemaPathNotifi.lastNodeIdentifier(), augm); + + when(notificationData.getType()).thenReturn(schemaPathNotifi); + when(notificationData.getBody()).thenReturn(notifiBody); + + final String result = prepareJson(notificationData, schemaPathNotifi); + + assertTrue(result.contains("ietf-restconf:notification")); + assertTrue(result.contains("event-time")); + assertTrue(result.contains("lf-augm" + '"' + ":" + '"' + "value")); + } + + private static AugmentationNode mockAugm(final LeafNode<String> leaf) { + final AugmentationNode augm = mock(AugmentationNode.class); + final AugmentationIdentifier augmId = new AugmentationIdentifier(Set.of(leaf.getIdentifier().getNodeType())); + when(augm.getIdentifier()).thenReturn(augmId); + + final Collection<DataContainerChild> childs = new ArrayList<>(); + childs.add(leaf); + + when(augm.body()).thenReturn(childs); + return augm; + } + + private static MapEntryNode mockMapEntry(final QName entryQName, final LeafNode<String> leaf) { + return Builders.mapEntryBuilder() + .withNodeIdentifier(NodeIdentifierWithPredicates.of(entryQName, leaf.getIdentifier().getNodeType(), + leaf.body())) + .withChild(leaf) + .build(); + } + + private static ContainerNode mockCont(final QName contQName, final DataContainerChild child) { + return Builders.containerBuilder() + .withNodeIdentifier(NodeIdentifier.create(contQName)) + .withChild(child) + .build(); + } + + private static LeafNode<String> mockLeaf(final QName leafQName) { + return ImmutableNodes.leafNode(leafQName, "value"); + } + + private String prepareJson(final DOMNotification notificationData, final Absolute schemaPathNotifi) { + final List<NotificationListenerAdapter> listNotifi = Notificator.createNotificationListener( + List.of(schemaPathNotifi), "stream-name", NotificationOutputType.JSON.toString(), controllerContext); + final NotificationListenerAdapter notifi = listNotifi.get(0); + return requireNonNull(notifi.prepareJson(schemaContext, notificationData)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilderTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilderTest.java new file mode 100644 index 0000000..f3ab6f9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/DatastoreIdentifierBuilderTest.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +public class DatastoreIdentifierBuilderTest { + + @Test(expected = IllegalArgumentException.class) + public void testDatastoreIdentifierBuilder() { + final DatastoreIdentifierBuilder datastoreIdentifierBuilder = new DatastoreIdentifierBuilder(); + assertNotNull(datastoreIdentifierBuilder); + DatastoreIdentifierBuilder.getDefaultInstance(""); + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleBuilderTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleBuilderTest.java new file mode 100644 index 0000000..a834b15 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleBuilderTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.google.common.collect.ImmutableSet; +import java.util.Set; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.RevisionIdentifier; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier; + +public class ModuleBuilderTest { + + @Test + public void testModuleBuilder() { + final ModuleBuilder moduleBuilder = new ModuleBuilder(); + final Module.Revision revision = new Module.Revision(new RevisionIdentifier("2016-10-11")); + final YangIdentifier yangIdentifierOne = new YangIdentifier("YangIdentifier1"); + final YangIdentifier yangIdentifierTwo = new YangIdentifier("YangIdentifier2"); + final Uri namespace = new Uri("namespace"); + final Set<YangIdentifier> yangIdentifierList = ImmutableSet.of(yangIdentifierOne, yangIdentifierTwo); + final ModuleKey moduleKeyOne = new ModuleKey(yangIdentifierOne, revision); + final ModuleKey moduleKeyTwo = new ModuleKey(moduleKeyOne); + moduleBuilder.setRevision(revision); + moduleBuilder.setDeviation(yangIdentifierList); + moduleBuilder.setFeature(yangIdentifierList); + moduleBuilder.setName(yangIdentifierOne); + moduleBuilder.setNamespace(namespace); + moduleBuilder.withKey(moduleKeyOne); + final Module moduleOne = moduleBuilder.build(); + final Module moduleTwo = new ModuleBuilder(moduleOne).build(); + + assertNotNull(moduleBuilder); + assertNotNull(revision); + assertNotNull(yangIdentifierOne); + assertNotNull(yangIdentifierTwo); + assertNotNull(namespace); + assertNotNull(yangIdentifierList); + assertNotNull(moduleKeyOne); + assertNotNull(moduleKeyOne.hashCode()); + assertNotNull(moduleKeyOne.toString()); + assertNotNull(moduleBuilder.toString()); + assertNotNull(moduleBuilder.hashCode()); + + assertEquals(moduleKeyOne, moduleKeyTwo); + assertEquals(revision, moduleKeyOne.getRevision()); + assertEquals(yangIdentifierOne, moduleKeyOne.getName()); + assertEquals(revision, moduleBuilder.getRevision()); + assertEquals(yangIdentifierList, moduleBuilder.getDeviation()); + assertEquals(yangIdentifierList, moduleBuilder.getFeature()); + assertEquals(yangIdentifierOne, moduleBuilder.getName()); + assertEquals(namespace, moduleBuilder.getNamespace()); + assertEquals(moduleKeyOne, moduleBuilder.key()); + assertEquals(moduleOne.toString(), moduleTwo.toString()); + assertEquals(moduleKeyOne.toString(), moduleKeyTwo.toString()); + + assertTrue(moduleOne.equals(moduleTwo)); + assertTrue(moduleKeyOne.equals(moduleKeyTwo)); + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilderTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilderTest.java new file mode 100644 index 0000000..578d2dd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/ModuleRevisionBuilderTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules.Module.Revision; + +public class ModuleRevisionBuilderTest { + + @Test + public void testModuleRevisionBuilder() { + final ModuleRevisionBuilder moduleRevisionBuilder = new ModuleRevisionBuilder(); + assertNotNull(moduleRevisionBuilder); + final Revision revision = ModuleRevisionBuilder.getDefaultInstance(""); + assertNotNull(revision); + assertEquals("", revision.getString()); + assertEquals(null, revision.getRevisionIdentifier()); + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilderTest.java b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilderTest.java new file mode 100644 index 0000000..5c2eba1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/restconf/rev131019/restconf/restconf/modules/RevisionBuilderTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.RevisionIdentifier; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.rev131019.restconf.restconf.modules.Module.Revision; + +public class RevisionBuilderTest { + @Test + public void testEmptyString() { + final RevisionBuilder revisionBuilder = new RevisionBuilder(); + assertNotNull(revisionBuilder); + final Revision revision = RevisionBuilder.getDefaultInstance(""); + validate(revision, "", null); + } + + @Test + public void testValidDataString() { + final String dateString = "2014-04-23"; + final Revision revision = RevisionBuilder.getDefaultInstance(dateString); + validate(revision, null, new RevisionIdentifier(dateString)); + } + + @Test(expected = IllegalArgumentException.class) + public void testNullString() { + RevisionBuilder.getDefaultInstance(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadFormatString() { + RevisionBuilder.getDefaultInstance("badFormat"); + } + + private static void validate(final Revision revisionUnderTest, final String expectedRevisionString, + final RevisionIdentifier expectedRevisionIdentifier) { + assertNotNull(revisionUnderTest); + assertEquals(expectedRevisionString, revisionUnderTest.getString()); + assertEquals(expectedRevisionIdentifier, revisionUnderTest.getRevisionIdentifier()); + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/dataa.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/dataa.json new file mode 100644 index 0000000..6097bd1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/dataa.json @@ -0,0 +1,7 @@ +{ + "main:cont":{ + "augment-main-a:cont1":{ + "lf11":"lf11 value from a" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/datab.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/datab.json new file mode 100644 index 0000000..297584a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/json/datab.json @@ -0,0 +1,7 @@ +{ + "main:cont":{ + "augment-main-b:cont1":{ + "lf11":"lf11 value from b" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/dataa.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/dataa.xml new file mode 100644 index 0000000..97c5ae5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/dataa.xml @@ -0,0 +1,5 @@ +<cont xmlns="ns:main"> + <cont1 xmlns="ns:augment:main:a"> + <lf11>lf11 value for a</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/datab.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/datab.xml new file mode 100644 index 0000000..b7696ca --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/xml/datab.xml @@ -0,0 +1,5 @@ +<cont xmlns="ns:main"> + <cont1 xmlns="ns:augment:main:b"> + <lf11>lf11 value for b</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-a.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-a.yang new file mode 100644 index 0000000..aa3bf3f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-a.yang @@ -0,0 +1,20 @@ +module augment-main-a { + namespace "ns:augment:main:a"; + prefix "aumaa"; + + + import main {prefix mn; revision-date 2014-01-21;} + + + revision "2014-01-21" { + } + + augment "/mn:cont" { + container cont1 { + leaf lf11 { + type string; + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-b.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-b.yang new file mode 100644 index 0000000..dcf493d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/augment-main-b.yang @@ -0,0 +1,20 @@ +module augment-main-b { + namespace "ns:augment:main:b"; + prefix "aumab"; + + + import main {prefix mn; revision-date 2014-01-21;} + + + revision "2014-01-21" { + } + + augment "/mn:cont" { + container cont1 { + leaf lf11 { + type string; + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/main.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/main.yang new file mode 100644 index 0000000..e291722 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/common/augment/yang/main.yang @@ -0,0 +1,10 @@ +module main { + namespace "ns:main"; + prefix "ma"; + + revision "2014-01-21" { + } + + container cont { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/opendaylight-inventory.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/opendaylight-inventory.yang new file mode 100644 index 0000000..e4247be --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/opendaylight-inventory.yang @@ -0,0 +1,19 @@ +module opendaylight-inventory { + namespace "urn:opendaylight:inventory"; + prefix inv; + + revision "2013-08-19" { + description "Initial revision of Inventory model"; + } + + + container nodes { + list node { + key "id"; + leaf id { + type string; + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote-augment.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote-augment.yang new file mode 100644 index 0000000..cdc8c69 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote-augment.yang @@ -0,0 +1,39 @@ +module sal-remote-augment { + + yang-version 1; + namespace "urn:sal:restconf:event:subscription"; + prefix "salrmt-aug-ev-subscr"; + + import sal-remote {prefix salrmt; revision-date "2014-01-14";} + + description + "Added input parameters to rpc create-data-change-event-subscription"; + + revision "2014-07-08" { + } + + augment "/salrmt:create-data-change-event-subscription/salrmt:input" { + leaf datastore { + type enumeration { + enum OPERATIONAL; + enum CONFIGURATION; + } + } + leaf scope { + type enumeration { + enum BASE; + enum ONE; + enum SUBTREE; + } + } + leaf notification-output-type { + type enumeration { + enum JSON; + enum XML; + } + default "XML"; + description "Input parameter which type of output will be parsed on notification"; + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote@2014-01-14.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote@2014-01-14.yang new file mode 100644 index 0000000..d12e252 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/datastore-and-scope-specification/sal-remote@2014-01-14.yang @@ -0,0 +1,98 @@ +module sal-remote { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; + prefix "sal-remote"; + + + organization "Cisco Systems, Inc."; + contact "Martin Bobak <mbobak@cisco.com>"; + + description + "This module contains the definition of methods related to + sal remote model. + + Copyright (c)2013 Cisco Systems, Inc. All rights reserved. + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2014-01-14" { + description + "Initial revision"; + } + + + typedef q-name { + type string; + reference + "http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#QName"; + } + + rpc create-data-change-event-subscription { + input { + leaf path { + type instance-identifier; + description "Subtree path. "; + } + } + output { + leaf stream-name { + type string; + description "Notification stream name."; + } + } + } + + notification data-changed-notification { + description "Data change notification."; + list data-change-event { + key path; + leaf path { + type instance-identifier; + } + leaf store { + type enumeration { + enum config; + enum operation; + } + } + leaf operation { + type enumeration { + enum created; + enum updated; + enum deleted; + } + } + anyxml data{ + description "DataObject "; + } + } + } + + rpc create-notification-stream { + input { + leaf-list notifications { + type q-name; + description "Notification QNames"; + } + } + output { + leaf notification-stream-identifier { + type string; + description "Unique notification stream identifier, in which notifications will be propagated"; + } + } + } + + rpc begin-transaction{ + output{ + anyxml data-modification-transaction{ + description "DataModificationTransaction xml"; + } + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/decoding-exception/yang/number.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/decoding-exception/yang/number.yang new file mode 100644 index 0000000..c463882 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/decoding-exception/yang/number.yang @@ -0,0 +1,17 @@ + module number { + + namespace "number"; + prefix "number"; + + revision 2014-04-24 { + } + + + + container cont { + leaf lf { + type uint8; + } + + } + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.json new file mode 100644 index 0000000..78b097d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.json @@ -0,0 +1,16 @@ +{ + "cont":{ + "cont1":[ + { + "lst11":{ + "lf111":"value1" + } + }, + { + "lst11":{ + "lf111":"value2" + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml new file mode 100644 index 0000000..26a1f5f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-container.xml @@ -0,0 +1,12 @@ +<cont> + <cont1> + <lst11> + <lf111>value1</lf111> + </lst11> + </cont1> + <cont1> + <lst11> + <lf111>value1</lf111> + </lst11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json new file mode 100644 index 0000000..d0a9fd4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.json @@ -0,0 +1,15 @@ +{ + "cont":{ + "cont1":{ + "lst11":[ + { + "lf111":"value1", + "lf111":"value2" + }, + { + "lf111":"value3" + } + ] + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml new file mode 100644 index 0000000..4221c2d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/equal-name-data-for-leaf.xml @@ -0,0 +1,11 @@ +<cont> + <cont1> + <lst11> + <lf111>value1</lf111>, + <lf111>value2</lf111>, + </lst11> + <lst11> + <lf111>value3</lf111>, + </lst11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang new file mode 100644 index 0000000..3a3653a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/equal-data-node-names/yang/equal-data-node-names.yang @@ -0,0 +1,18 @@ +/* bug 1204 */ +module equal-data-node-names { + namespace "ns:equal:data:node:names"; + + prefix "eqdanona"; + revision 2014-06-26 { + } + + container cont { + container cont1 { + list lst11 { + leaf lf111 { + type string; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/foo.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/foo.xml new file mode 100644 index 0000000..96b69c4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/foo.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<top-level-list xmlns="foo">
+ <key-leaf>key-value</key-leaf>
+ <ordinary-leaf>leaf-value</ordinary-leaf>
+</top-level-list>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/yang/foo.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/yang/foo.yang new file mode 100644 index 0000000..ceed3d9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/foo-xml-test/yang/foo.yang @@ -0,0 +1,18 @@ +module foo {
+ namespace foo;
+ prefix foo;
+
+ revision 2017-08-09;
+
+ list top-level-list {
+ key key-leaf;
+
+ leaf key-leaf {
+ type string;
+ }
+
+ leaf ordinary-leaf {
+ type string;
+ }
+ }
+}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/make-toast-rpc-input.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/make-toast-rpc-input.json new file mode 100644 index 0000000..1bba146 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/make-toast-rpc-input.json @@ -0,0 +1,7 @@ +{ + "input" : + { + "toaster:toasterDoneness" : "10", + "toaster:toasterToastType": "wheat-bread" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.json new file mode 100644 index 0000000..0eae37a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.json @@ -0,0 +1,10 @@ +{ + "test-module:input":{ + "cont":{ + "cont1":{ + "lf11":"lf1 data", + "lf12":"lf2 data" + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.xml new file mode 100644 index 0000000..fb9726d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data-rpc-input.xml @@ -0,0 +1,8 @@ +<input xmlns="test:module"> + <cont> + <cont1> + <lf11>lf1 data</lf11> + <lf12>lf2 data</lf12> + </cont1> + </cont> +</input>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data2.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data2.xml new file mode 100644 index 0000000..db2f4e9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data2.xml @@ -0,0 +1,6 @@ +<cont xmlns="test:module"> + <cont1> + <lf11>lf1 data</lf11> + <lf12>lf2 data</lf12> + </cont1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data3.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data3.xml new file mode 100644 index 0000000..b7b05d1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data3.xml @@ -0,0 +1,4 @@ +<cont1 xmlns="test:module"> + <lf11>lf1 data</lf11> + <lf12>lf2 data</lf12> +</cont1> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data4.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data4.xml new file mode 100644 index 0000000..3ce6741 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data4.xml @@ -0,0 +1,6 @@ + +<cont1 xmlns="test:module"> + <lf11>lf1 data</lf11> + <lf12>lf2 data</lf12> +</cont1> + diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data5.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data5.xml new file mode 100644 index 0000000..89aa782 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data5.xml @@ -0,0 +1,8 @@ +<interfaces xmlns="test:module"> + <class> + <name>John</name> + <address>F.C.I 43</address> + <email>j@j</email> + </class> +</interfaces> + diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data6.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data6.xml new file mode 100644 index 0000000..7feb6bf --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data6.xml @@ -0,0 +1,6 @@ +<class xmlns="test:module"> + <name>John</name> + <address>F.C.I 43</address> + <email>j@j</email> +</class> + diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data7.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data7.xml new file mode 100644 index 0000000..4374277 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-data2/data7.xml @@ -0,0 +1,8 @@ +<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <cont xmlns="test:module"> + <cont1> + <lf11>lf1 data</lf11> + <lf12>lf2 data</lf12> + </cont1> + </cont> +</data>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-module/test-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-module/test-module.yang new file mode 100644 index 0000000..2cc78b3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/test-module/test-module.yang @@ -0,0 +1,101 @@ +module test-module { + namespace "test:module"; + prefix tstmod; + + revision 2014-01-09 { + } + + identity module-type { + } + + identity test-identity { + } + + container interfaces { + container class { + leaf name { + type string; + } + leaf address { + type string; + } + leaf email { + type string; + } + } + } + + container cont { + container cont1 { + leaf lf11 { + type string; + } + leaf lf12 { + type string; + } + } + list lst1 { + key "lf11"; + leaf lf11 { + type string; + } + } + } + + container modules { + list module { + key "type name"; + leaf name { + type string; + mandatory true; + } + + leaf type { + type identityref { + base module-type; + } + mandatory true; + } + + leaf data { + type string; + } + } + } + + list lst-with-composite-key { + key "key1 key2"; + leaf key1 { + type string; + } + leaf key2 { + type uint8; + } + } + + rpc no-payload-rpc-test { + output { + container cont-output { + } + } + } + + rpc rpc-test { + input { + container cont { + container cont1 { + leaf lf11 { + type string; + } + leaf lf12 { + type string; + } + } + } + } + output { + container cont-output { + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1Data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1Data.json new file mode 100644 index 0000000..c7554f7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1Data.json @@ -0,0 +1,6 @@ +{ + "cont1": { + "lf11": "lf11 data", + "lf12": "lf12 data" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1DataPatch.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1DataPatch.json new file mode 100644 index 0000000..a8d6799 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testCont1DataPatch.json @@ -0,0 +1,20 @@ +{ + "ietf-restconf:yang-patch" : { + "patch-id" : "0", + "edit" : [ + { + "edit-id" : "edit1", + "operation" : "create", + "target" : "", + "value" : + { + "cont1": + { + "lf11": "lf11 data", + "lf12": "lf12 data" + } + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.json new file mode 100644 index 0000000..a736067 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.json @@ -0,0 +1,92 @@ +{ + "interfaces": { + "interface": [ + { + "name": "eth0", + "type": "ethernetCsmacd", + "enabled": false + }, + { + "name": "eth1", + "type": "ethernetCsmacd", + "enabled": true, + "vlan-tagging": true + }, + { + "name": "eth1.10", + "type": "l2vlan", + "enabled": true, + "base-interface": "eth1", + "vlan-id": 10 + }, + { + "name": "lo1", + "type": "softwareLoopback", + "enabled": true + } + ] + }, + "interfaces-state": { + "interface": [ + { + "name": "eth0", + "type": "ethernetCsmacd", + "admin-status": "down", + "oper-status": "down", + "if-index": 2, + "phys-address": "00:01:02:03:04:05", + "statistics": { + "discontinuity-time": "2013-04-01T03:00:00+00:00" + } + }, + { + "name": "eth1", + "type": "ethernetCsmacd", + "admin-status": "up", + "oper-status": "up", + "if-index": 7, + "phys-address": "00:01:02:03:04:06", + "higher-layer-if": [ + "eth1.10" + ], + "statistics": { + "discontinuity-time": "2013-04-01T03:00:00+00:00" + } + }, + { + "name": "eth1.10", + "type": "l2vlan", + "admin-status": "up", + "oper-status": "up", + "if-index": 9, + "lower-layer-if": [ + "eth1" + ], + "statistics": { + "discontinuity-time": "2013-04-01T03:00:00+00:00" + } + }, + { + "name": "eth2", + "type": "ethernetCsmacd", + "admin-status": "down", + "oper-status": "down", + "if-index": 8, + "phys-address": "00:01:02:03:04:07", + "statistics": { + "discontinuity-time": "2013-04-01T03:00:00+00:00" + } + }, + { + "name": "lo1", + "type": "softwareLoopback", + "admin-status": "up", + "oper-status": "up", + "if-index": 1, + "statistics": { + "discontinuity-time": "2013-04-01T03:00:00+00:00" + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.xml new file mode 100644 index 0000000..bdd9c10 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/testData.xml @@ -0,0 +1,120 @@ +<rpc-reply + xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" + message-id="101"> + <data> + + <interfaces + xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" + xmlns:vlan="http://example.com/vlan"> + + <interface> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + </interface> + + <interface> + <name>eth1</name> + <type>ethernetCsmacd</type> + <enabled>true</enabled> + <vlan:vlan-tagging>true</vlan:vlan-tagging> + </interface> + + <interface> + <name>eth1.10</name> + <type>l2vlan</type> + <enabled>true</enabled> + <vlan:base-interface>eth1</vlan:base-interface> + <vlan:vlan-id>10</vlan:vlan-id> + </interface> + + <interface> + <name>lo1</name> + <type>softwareLoopback</type> + <enabled>true</enabled> + </interface> + + </interfaces> + + <interfaces-state + xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> + + <interface> + <name>eth0</name> + <type>ethernetCsmacd</type> + <admin-status>down</admin-status> + <oper-status>down</oper-status> + <if-index>2</if-index> + <phys-address>00:01:02:03:04:05</phys-address> + <statistics> + <discontinuity-time> + 2013-04-01T03:00:00+00:00 + </discontinuity-time> + <!-- counters now shown here --> + </statistics> + </interface> + + <interface> + <name>eth1</name> + <type>ethernetCsmacd</type> + <admin-status>up</admin-status> + <oper-status>up</oper-status> + <if-index>7</if-index> + <phys-address>00:01:02:03:04:06</phys-address> + <higher-layer-if>eth1.10</higher-layer-if> + <statistics> + <discontinuity-time> + 2013-04-01T03:00:00+00:00 + </discontinuity-time> + <!-- counters now shown here --> + </statistics> + </interface> + + <interface> + <name>eth1.10</name> + <type>l2vlan</type> + <admin-status>up</admin-status> + <oper-status>up</oper-status> + <if-index>9</if-index> + <lower-layer-if>eth1</lower-layer-if> + <statistics> + <discontinuity-time> + 2013-04-01T03:00:00+00:00 + </discontinuity-time> + <!-- counters now shown here --> + </statistics> + </interface> + + <!-- This interface is not configured --> + <interface> + <name>eth2</name> + <type>ethernetCsmacd</type> + <admin-status>down</admin-status> + <oper-status>down</oper-status> + <if-index>8</if-index> + <phys-address>00:01:02:03:04:07</phys-address> + <statistics> + <discontinuity-time> + 2013-04-01T03:00:00+00:00 + </discontinuity-time> + <!-- counters now shown here --> + </statistics> + </interface> + + <interface> + <name>lo1</name> + <type>softwareLoopback</type> + <admin-status>up</admin-status> + <oper-status>up</oper-status> + <if-index>1</if-index> + <statistics> + <discontinuity-time> + 2013-04-01T03:00:00+00:00 + </discontinuity-time> + <!-- counters now shown here --> + </statistics> + </interface> + + </interfaces-state> + </data> +</rpc-reply>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ex-vlan.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ex-vlan.yang new file mode 100644 index 0000000..59369ad --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ex-vlan.yang @@ -0,0 +1,47 @@ +module ex-vlan { + namespace "http://example.com/vlan"; + prefix "vlan"; + + import ietf-interfaces { + prefix if; + } + + revision 2013-10-22 { + description + "Initial revision."; + reference + "RFC A YANG Data Model for Interface Management draft-ietf-netmod-interfaces-cfg-12 - Appendix C"; + } + + augment "/if:interfaces/if:interface" { + when "if:type = 'ethernetCsmacd' or + if:type = 'ieee8023adLag'"; + leaf vlan-tagging { + type boolean; + default false; + } + } + + augment "/if:interfaces/if:interface" { + when "if:type = 'l2vlan'"; + + leaf base-interface { + type if:interface-ref; + must "/if:interfaces/if:interface[if:name = current()]" + + "/vlan:vlan-tagging = 'true'" { + description + "The base interface must have vlan tagging enabled."; + } + } + leaf vlan-id { + type uint16 { + range "1..4094"; + } + must "../base-interface" { + description + "If a vlan-id is defined, a base-interface must + be specified."; + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/iana-if-type.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/iana-if-type.yang new file mode 100644 index 0000000..7bd0003 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/iana-if-type.yang @@ -0,0 +1,1517 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org"; + description + "This YANG module defines the iana-if-type typedef, which + contains YANG definitions for IANA-registered interface types. + + This YANG module is maintained by IANA, and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: IANA Interface Type YANG Module"; + } + + typedef iana-if-type { + type enumeration { + enum "other" { + value 1; + description + "None of the following"; + } + enum "regular1822" { + value 2; + } + enum "hdh1822" { + value 3; + } + enum "ddnX25" { + value 4; + } + enum "rfc877x25" { + value 5; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + enum "ethernetCsmacd" { + value 6; + description + "For all ethernet-like interfaces, regardless of speed, + as per RFC3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88023Csmacd" { + value 7; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88024TokenBus" { + value 8; + } + enum "iso88025TokenRing" { + value 9; + } + enum "iso88026Man" { + value 10; + } + enum "starLan" { + value 11; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "proteon10Mbit" { + value 12; + } + enum "proteon80Mbit" { + value 13; + } + enum "hyperchannel" { + value 14; + } + enum "fddi" { + value 15; + reference + "RFC 1512 - FDDI Management Information Base"; + } + enum "lapb" { + value 16; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + enum "sdlc" { + value 17; + } + enum "ds1" { + value 18; + description + "DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "e1" { + value 19; + status obsolete; + description + "Obsolete see DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "basicISDN" { + value 20; + description + "see also RFC2127"; + } + enum "primaryISDN" { + value 21; + } + enum "propPointToPointSerial" { + value 22; + description + "proprietary serial"; + } + enum "ppp" { + value 23; + } + enum "softwareLoopback" { + value 24; + } + enum "eon" { + value 25; + description + "CLNP over IP"; + } + enum "ethernet3Mbit" { + value 26; + } + enum "nsip" { + value 27; + description + "XNS over IP"; + } + enum "slip" { + value 28; + description + "generic SLIP"; + } + enum "ultra" { + value 29; + description + "ULTRA technologies"; + } + enum "ds3" { + value 30; + description + "DS3-MIB"; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + enum "sip" { + value 31; + description + "SMDS, coffee"; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + enum "frameRelay" { + value 32; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + enum "rs232" { + value 33; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + enum "para" { + value 34; + description + "parallel-port"; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + enum "arcnet" { + value 35; + description + "arcnet"; + } + enum "arcnetPlus" { + value 36; + description + "arcnet plus"; + } + enum "atm" { + value 37; + description + "ATM cells"; + } + enum "miox25" { + value 38; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + enum "sonet" { + value 39; + description + "SONET or SDH"; + } + enum "x25ple" { + value 40; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + enum "iso88022llc" { + value 41; + } + enum "localTalk" { + value 42; + } + enum "smdsDxi" { + value 43; + } + enum "frameRelayService" { + value 44; + description + "FRNETSERV-MIB"; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + enum "v35" { + value 45; + } + enum "hssi" { + value 46; + } + enum "hippi" { + value 47; + } + enum "modem" { + value 48; + description + "Generic modem"; + } + enum "aal5" { + value 49; + description + "AAL5 over ATM"; + } + enum "sonetPath" { + value 50; + } + enum "sonetVT" { + value 51; + } + enum "smdsIcip" { + value 52; + description + "SMDS InterCarrier Interface"; + } + enum "propVirtual" { + value 53; + description + "proprietary virtual/internal"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "propMultiplexor" { + value 54; + description + "proprietary multiplexing"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "ieee80212" { + value 55; + description + "100BaseVG"; + } + enum "fibreChannel" { + value 56; + description + "Fibre Channel"; + } + enum "hippiInterface" { + value 57; + description + "HIPPI interfaces"; + } + enum "frameRelayInterconnect" { + value 58; + status obsolete; + description + "Obsolete use either + frameRelay(32) or frameRelayService(44)."; + } + enum "aflane8023" { + value 59; + description + "ATM Emulated LAN for 802.3"; + } + enum "aflane8025" { + value 60; + description + "ATM Emulated LAN for 802.5"; + } + enum "cctEmul" { + value 61; + description + "ATM Emulated circuit"; + } + enum "fastEther" { + value 62; + status deprecated; + description + "Obsoleted via RFC3635. + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "isdn" { + value 63; + description + "ISDN and X.25"; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + enum "v11" { + value 64; + description + "CCITT V.11/X.21"; + } + enum "v36" { + value 65; + description + "CCITT V.36"; + } + enum "g703at64k" { + value 66; + description + "CCITT G703 at 64Kbps"; + } + enum "g703at2mb" { + value 67; + status obsolete; + description + "Obsolete see DS1-MIB"; + } + enum "qllc" { + value 68; + description + "SNA QLLC"; + } + enum "fastEtherFX" { + value 69; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "channel" { + value 70; + description + "channel"; + } + enum "ieee80211" { + value 71; + description + "radio spread spectrum"; + } + enum "ibm370parChan" { + value 72; + description + "IBM System 360/370 OEMI Channel"; + } + enum "escon" { + value 73; + description + "IBM Enterprise Systems Connection"; + } + enum "dlsw" { + value 74; + description + "Data Link Switching"; + } + enum "isdns" { + value 75; + description + "ISDN S/T interface"; + } + enum "isdnu" { + value 76; + description + "ISDN U interface"; + } + enum "lapd" { + value 77; + description + "Link Access Protocol D"; + } + enum "ipSwitch" { + value 78; + description + "IP Switching Objects"; + } + enum "rsrb" { + value 79; + description + "Remote Source Route Bridging"; + } + enum "atmLogical" { + value 80; + description + "ATM Logical Port"; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + enum "ds0" { + value 81; + description + "Digital Signal Level 0"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "ds0Bundle" { + value 82; + description + "group of ds0s on the same ds1"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "bsc" { + value 83; + description + "Bisynchronous Protocol"; + } + enum "async" { + value 84; + description + "Asynchronous Protocol"; + } + enum "cnr" { + value 85; + description + "Combat Net Radio"; + } + enum "iso88025Dtr" { + value 86; + description + "ISO 802.5r DTR"; + } + enum "eplrs" { + value 87; + description + "Ext Pos Loc Report Sys"; + } + enum "arap" { + value 88; + description + "Appletalk Remote Access Protocol"; + } + enum "propCnls" { + value 89; + description + "Proprietary Connectionless Protocol"; + } + enum "hostPad" { + value 90; + description + "CCITT-ITU X.29 PAD Protocol"; + } + enum "termPad" { + value 91; + description + "CCITT-ITU X.3 PAD Facility"; + } + enum "frameRelayMPI" { + value 92; + description + "Multiproto Interconnect over FR"; + } + enum "x213" { + value 93; + description + "CCITT-ITU X213"; + } + enum "adsl" { + value 94; + description + "Asymmetric Digital Subscriber Loop"; + } + enum "radsl" { + value 95; + description + "Rate-Adapt. Digital Subscriber Loop"; + } + enum "sdsl" { + value 96; + description + "Symmetric Digital Subscriber Loop"; + } + enum "vdsl" { + value 97; + description + "Very H-Speed Digital Subscrib. Loop"; + } + enum "iso88025CRFPInt" { + value 98; + description + "ISO 802.5 CRFP"; + } + enum "myrinet" { + value 99; + description + "Myricom Myrinet"; + } + enum "voiceEM" { + value 100; + description + "voice recEive and transMit"; + } + enum "voiceFXO" { + value 101; + description + "voice Foreign Exchange Office"; + } + enum "voiceFXS" { + value 102; + description + "voice Foreign Exchange Station"; + } + enum "voiceEncap" { + value 103; + description + "voice encapsulation"; + } + enum "voiceOverIp" { + value 104; + description + "voice over IP encapsulation"; + } + enum "atmDxi" { + value 105; + description + "ATM DXI"; + } + enum "atmFuni" { + value 106; + description + "ATM FUNI"; + } + enum "atmIma" { + value 107; + description + "ATM IMA"; + } + enum "pppMultilinkBundle" { + value 108; + description + "PPP Multilink Bundle"; + } + enum "ipOverCdlc" { + value 109; + description + "IBM ipOverCdlc"; + } + enum "ipOverClaw" { + value 110; + description + "IBM Common Link Access to Workstn"; + } + enum "stackToStack" { + value 111; + description + "IBM stackToStack"; + } + enum "virtualIpAddress" { + value 112; + description + "IBM VIPA"; + } + enum "mpc" { + value 113; + description + "IBM multi-protocol channel support"; + } + enum "ipOverAtm" { + value 114; + description + "IBM ipOverAtm"; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + enum "iso88025Fiber" { + value 115; + description + "ISO 802.5j Fiber Token Ring"; + } + enum "tdlc" { + value 116; + description + "IBM twinaxial data link control"; + } + enum "gigabitEthernet" { + value 117; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "hdlc" { + value 118; + description + "HDLC"; + } + enum "lapf" { + value 119; + description + "LAP F"; + } + enum "v37" { + value 120; + description + "V.37"; + } + enum "x25mlp" { + value 121; + description + "Multi-Link Protocol"; + } + enum "x25huntGroup" { + value 122; + description + "X25 Hunt Group"; + } + enum "transpHdlc" { + value 123; + description + "Transp HDLC"; + } + enum "interleave" { + value 124; + description + "Interleave channel"; + } + enum "fast" { + value 125; + description + "Fast channel"; + } + enum "ip" { + value 126; + description + "IP (for APPN HPR in IP networks)"; + } + enum "docsCableMaclayer" { + value 127; + description + "CATV Mac Layer"; + } + enum "docsCableDownstream" { + value 128; + description + "CATV Downstream interface"; + } + enum "docsCableUpstream" { + value 129; + description + "CATV Upstream interface"; + } + enum "a12MppSwitch" { + value 130; + description + "Avalon Parallel Processor"; + } + enum "tunnel" { + value 131; + description + "Encapsulation interface"; + } + enum "coffee" { + value 132; + description + "coffee pot"; + reference + "RFC 2325 - Coffee MIB"; + } + enum "ces" { + value 133; + description + "Circuit Emulation Service"; + } + enum "atmSubInterface" { + value 134; + description + "ATM Sub Interface"; + } + enum "l2vlan" { + value 135; + description + "Layer 2 Virtual LAN using 802.1Q"; + } + enum "l3ipvlan" { + value 136; + description + "Layer 3 Virtual LAN using IP"; + } + enum "l3ipxvlan" { + value 137; + description + "Layer 3 Virtual LAN using IPX"; + } + enum "digitalPowerline" { + value 138; + description + "IP over Power Lines"; + } + enum "mediaMailOverIp" { + value 139; + description + "Multimedia Mail over IP"; + } + enum "dtm" { + value 140; + description + "Dynamic syncronous Transfer Mode"; + } + enum "dcn" { + value 141; + description + "Data Communications Network"; + } + enum "ipForward" { + value 142; + description + "IP Forwarding Interface"; + } + enum "msdsl" { + value 143; + description + "Multi-rate Symmetric DSL"; + } + enum "ieee1394" { + value 144; + description + "IEEE1394 High Performance Serial Bus"; + } + enum "if-gsn" { + value 145; + description + "HIPPI-6400"; + } + enum "dvbRccMacLayer" { + value 146; + description + "DVB-RCC MAC Layer"; + } + enum "dvbRccDownstream" { + value 147; + description + "DVB-RCC Downstream Channel"; + } + enum "dvbRccUpstream" { + value 148; + description + "DVB-RCC Upstream Channel"; + } + enum "atmVirtual" { + value 149; + description + "ATM Virtual Interface"; + } + enum "mplsTunnel" { + value 150; + description + "MPLS Tunnel Virtual Interface"; + } + enum "srp" { + value 151; + description + "Spatial Reuse Protocol"; + } + enum "voiceOverAtm" { + value 152; + description + "Voice Over ATM"; + } + enum "voiceOverFrameRelay" { + value 153; + description + "Voice Over Frame Relay"; + } + enum "idsl" { + value 154; + description + "Digital Subscriber Loop over ISDN"; + } + enum "compositeLink" { + value 155; + description + "Avici Composite Link Interface"; + } + enum "ss7SigLink" { + value 156; + description + "SS7 Signaling Link"; + } + enum "propWirelessP2P" { + value 157; + description + "Prop. P2P wireless interface"; + } + enum "frForward" { + value 158; + description + "Frame Forward Interface"; + } + enum "rfc1483" { + value 159; + description + "Multiprotocol over ATM AAL5"; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + enum "usb" { + value 160; + description + "USB Interface"; + } + enum "ieee8023adLag" { + value 161; + description + "IEEE 802.3ad Link Aggregate"; + } + enum "bgppolicyaccounting" { + value 162; + description + "BGP Policy Accounting"; + } + enum "frf16MfrBundle" { + value 163; + description + "FRF .16 Multilink Frame Relay"; + } + enum "h323Gatekeeper" { + value 164; + description + "H323 Gatekeeper"; + } + enum "h323Proxy" { + value 165; + description + "H323 Voice and Video Proxy"; + } + enum "mpls" { + value 166; + description + "MPLS"; + } + enum "mfSigLink" { + value 167; + description + "Multi-frequency signaling link"; + } + enum "hdsl2" { + value 168; + description + "High Bit-Rate DSL - 2nd generation"; + } + enum "shdsl" { + value 169; + description + "Multirate HDSL2"; + } + enum "ds1FDL" { + value 170; + description + "Facility Data Link 4Kbps on a DS1"; + } + enum "pos" { + value 171; + description + "Packet over SONET/SDH Interface"; + } + enum "dvbAsiIn" { + value 172; + description + "DVB-ASI Input"; + } + enum "dvbAsiOut" { + value 173; + description + "DVB-ASI Output"; + } + enum "plc" { + value 174; + description + "Power Line Communtications"; + } + enum "nfas" { + value 175; + description + "Non Facility Associated Signaling"; + } + enum "tr008" { + value 176; + description + "TR008"; + } + enum "gr303RDT" { + value 177; + description + "Remote Digital Terminal"; + } + enum "gr303IDT" { + value 178; + description + "Integrated Digital Terminal"; + } + enum "isup" { + value 179; + description + "ISUP"; + } + enum "propDocsWirelessMaclayer" { + value 180; + description + "Cisco proprietary Maclayer"; + } + enum "propDocsWirelessDownstream" { + value 181; + description + "Cisco proprietary Downstream"; + } + enum "propDocsWirelessUpstream" { + value 182; + description + "Cisco proprietary Upstream"; + } + enum "hiperlan2" { + value 183; + description + "HIPERLAN Type 2 Radio Interface"; + } + enum "propBWAp2Mp" { + value 184; + description + "PropBroadbandWirelessAccesspt2multipt use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated and ieee80216WMAN(237) should be used + instead."; + } + enum "sonetOverheadChannel" { + value 185; + description + "SONET Overhead Channel"; + } + enum "digitalWrapperOverheadChannel" { + value 186; + description + "Digital Wrapper"; + } + enum "aal2" { + value 187; + description + "ATM adaptation layer 2"; + } + enum "radioMAC" { + value 188; + description + "MAC layer over radio links"; + } + enum "atmRadio" { + value 189; + description + "ATM over radio links"; + } + enum "imt" { + value 190; + description + "Inter Machine Trunks"; + } + enum "mvl" { + value 191; + description + "Multiple Virtual Lines DSL"; + } + enum "reachDSL" { + value 192; + description + "Long Reach DSL"; + } + enum "frDlciEndPt" { + value 193; + description + "Frame Relay DLCI End Point"; + } + enum "atmVciEndPt" { + value 194; + description + "ATM VCI End Point"; + } + enum "opticalChannel" { + value 195; + description + "Optical Channel"; + } + enum "opticalTransport" { + value 196; + description + "Optical Transport"; + } + enum "propAtm" { + value 197; + description + "Proprietary ATM"; + } + enum "voiceOverCable" { + value 198; + description + "Voice Over Cable Interface"; + } + enum "infiniband" { + value 199; + description + "Infiniband"; + } + enum "teLink" { + value 200; + description + "TE Link"; + } + enum "q2931" { + value 201; + description + "Q.2931"; + } + enum "virtualTg" { + value 202; + description + "Virtual Trunk Group"; + } + enum "sipTg" { + value 203; + description + "SIP Trunk Group"; + } + enum "sipSig" { + value 204; + description + "SIP Signaling"; + } + enum "docsCableUpstreamChannel" { + value 205; + description + "CATV Upstream Channel"; + } + enum "econet" { + value 206; + description + "Acorn Econet"; + } + enum "pon155" { + value 207; + description + "FSAN 155Mb Symetrical PON interface"; + } + enum "pon622" { + value 208; + description + "FSAN622Mb Symetrical PON interface"; + } + enum "bridge" { + value 209; + description + "Transparent bridge interface"; + } + enum "linegroup" { + value 210; + description + "Interface common to multiple lines"; + } + enum "voiceEMFGD" { + value 211; + description + "voice E&M Feature Group D"; + } + enum "voiceFGDEANA" { + value 212; + description + "voice FGD Exchange Access North American"; + } + enum "voiceDID" { + value 213; + description + "voice Direct Inward Dialing"; + } + enum "mpegTransport" { + value 214; + description + "MPEG transport interface"; + } + enum "sixToFour" { + value 215; + status deprecated; + description + "6to4 interface (DEPRECATED)"; + reference + "RFC 4087 - IP Tunnel MIB"; + } + enum "gtp" { + value 216; + description + "GTP (GPRS Tunneling Protocol)"; + } + enum "pdnEtherLoop1" { + value 217; + description + "Paradyne EtherLoop 1"; + } + enum "pdnEtherLoop2" { + value 218; + description + "Paradyne EtherLoop 2"; + } + enum "opticalChannelGroup" { + value 219; + description + "Optical Channel Group"; + } + enum "homepna" { + value 220; + description + "HomePNA ITU-T G.989"; + } + enum "gfp" { + value 221; + description + "Generic Framing Procedure (GFP)"; + } + enum "ciscoISLvlan" { + value 222; + description + "Layer 2 Virtual LAN using Cisco ISL"; + } + enum "actelisMetaLOOP" { + value 223; + description + "Acteleis proprietary MetaLOOP High Speed Link"; + } + enum "fcipLink" { + value 224; + description + "FCIP Link"; + } + enum "rpr" { + value 225; + description + "Resilient Packet Ring Interface Type"; + } + enum "qam" { + value 226; + description + "RF Qam Interface"; + } + enum "lmp" { + value 227; + description + "Link Management Protocol"; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + enum "cblVectaStar" { + value 228; + description + "Cambridge Broadband Networks Limited VectaStar"; + } + enum "docsCableMCmtsDownstream" { + value 229; + description + "CATV Modular CMTS Downstream Interface"; + } + enum "adsl2" { + value 230; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)"; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + enum "macSecControlledIF" { + value 231; + description + "MACSecControlled"; + } + enum "macSecUncontrolledIF" { + value 232; + description + "MACSecUncontrolled"; + } + enum "aviciOpticalEther" { + value 233; + description + "Avici Optical Ethernet Aggregate"; + } + enum "atmbond" { + value 234; + description + "atmbond"; + } + enum "voiceFGDOS" { + value 235; + description + "voice FGD Operator Services"; + } + enum "mocaVersion1" { + value 236; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA"; + } + enum "ieee80216WMAN" { + value 237; + description + "IEEE 802.16 WMAN interface"; + } + enum "adsl2plus" { + value 238; + description + "Asymmetric Digital Subscriber Loop Version 2, + Version 2 Plus and all variants"; + } + enum "dvbRcsMacLayer" { + value 239; + description + "DVB-RCS MAC Layer"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbTdm" { + value 240; + description + "DVB Satellite TDM"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbRcsTdma" { + value 241; + description + "DVB-RCS TDMA"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "x86Laps" { + value 242; + description + "LAPS based on ITU-T X.86/Y.1323"; + } + enum "wwanPP" { + value 243; + description + "3GPP WWAN"; + } + enum "wwanPP2" { + value 244; + description + "3GPP2 WWAN"; + } + enum "voiceEBS" { + value 245; + description + "voice P-phone EBS physical interface"; + } + enum "ifPwType" { + value 246; + description + "Pseudowire interface type"; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base"; + } + enum "ilan" { + value 247; + description + "Internal LAN on a bridge per IEEE 802.1ap"; + } + enum "pip" { + value 248; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB"; + } + enum "aluELP" { + value 249; + description + "Alcatel-Lucent Ethernet Link Protection"; + } + enum "gpon" { + value 250; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948"; + } + enum "vdsl2" { + value 251; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)"; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + enum "capwapDot11Profile" { + value 252; + description + "WLAN Profile Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapDot11Bss" { + value 253; + description + "WLAN BSS Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapWtpVirtualRadio" { + value 254; + description + "WTP Virtual Radio Interface"; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + enum "bits" { + value 255; + description + "bitsport"; + } + enum "docsCableUpstreamRfPort" { + value 256; + description + "DOCSIS CATV Upstream RF Port"; + } + enum "cableDownstreamRfPort" { + value 257; + description + "CATV downstream RF port"; + } + enum "vmwareVirtualNic" { + value 258; + description + "VMware Virtual Network Interface"; + } + enum "ieee802154" { + value 259; + description + "IEEE 802.15.4 WPAN interface"; + reference + "IEEE 802.15.4-2006"; + } + enum "otnOdu" { + value 260; + description + "OTN Optical Data Unit"; + } + enum "otnOtu" { + value 261; + description + "OTN Optical channel Transport Unit"; + } + enum "ifVfiType" { + value 262; + description + "VPLS Forwarding Instance Interface Type"; + } + enum "g9981" { + value 263; + description + "G.998.1 bonded interface"; + } + enum "g9982" { + value 264; + description + "G.998.2 bonded interface"; + } + enum "g9983" { + value 265; + description + "G.998.3 bonded interface"; + } + enum "aluEpon" { + value 266; + description + "Ethernet Passive Optical Networks (E-PON)"; + } + enum "aluEponOnu" { + value 267; + description + "EPON Optical Network Unit"; + } + enum "aluEponPhysicalUni" { + value 268; + description + "EPON physical User to Network interface"; + } + enum "aluEponLogicalLink" { + value 269; + description + "The emulation of a point-to-point link over the EPON + layer"; + } + enum "aluGponOnu" { + value 270; + description + "GPON Optical Network Unit"; + reference + "ITU-T G.984.2"; + } + enum "aluGponPhysicalUni" { + value 271; + description + "GPON physical User to Network interface"; + reference + "ITU-T G.984.2"; + } + enum "vmwareNicTeam" { + value 272; + description + "VMware NIC Team"; + } + // value 273 reserved by IANA + } + description + "This data type is used as the syntax of the 'type' + leaf in the 'interface' list in the YANG module + ietf-interface. + + The definition of this typedef with the + addition of newly assigned values is published + periodically by the IANA, in either the Assigned + Numbers RFC, or some derivative of it specific to + Internet Network Management number assignments. (The + latest arrangements can be obtained by contacting the + IANA.) + + Requests for new values should be made to IANA via + email (iana&iana.org)."; + reference + "IANA ifType definitions registry. + <http://www.iana.org/assignments/smi-numbers>"; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-interfaces@2013-07-04.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-interfaces@2013-07-04.yang new file mode 100644 index 0000000..9db753c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-interfaces@2013-07-04.yang @@ -0,0 +1,673 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + import iana-if-type { + prefix ianaift; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for Interface Management"; + } + + /* Typedefs */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* Features */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* Data nodes */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request, if the implementation does not support + pre-provisioning of interfaces, or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the feature 'pre-provisioning' is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the feature 'arbitrary-names' is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list. Since the name in that + list MAY be mapped to ifName by an implementation, such an + implementation MUST restrict the allowed values for this + leaf so that it matches the restrictions of ifName. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + This leaf MAY be mapped to ifAlias by an implementation. + Such an implementation MUST restrict the allowed values + for this leaf so that it matches the restrictions of + ifAlias. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation if + it modifies this single leaf in 'startup', or if it + performs an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces which do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + This leaf MAY be mapped to ifName by an implementation."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a MAC address. The interface's media-specific + modules must define the bit and byte ordering and the + format of the value of this object. For interfaces that do + not have such an address (e.g., a serial line), this node + is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits / second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface which were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing the number of transmission + units received via the interface which were discarded + because of an unknown or unsupported protocol. For any + interface that does not support protocol multiplexing, + this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-yang-types@2013-05-16.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-yang-types@2013-05-16.yang new file mode 100644 index 0000000..6c82d9d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/ietf-yang-types@2013-05-16.yang @@ -0,0 +1,471 @@ +module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + revision 2013-05-16 { + description + "This revision adds the following new data types: + - yang-identifier + - hex-string + - uuid + - dotted-quad"; + reference + "RFC XXXX: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined in RFC 6020, page 163. + An identifier must start with an alphabetic character or + an underscore followed by an arbitrary sequence of + alphabetic or numeric characters, underscores, hyphens + or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lower-case or upper-case character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using + the time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually + referring to the notion of local time) uses the time-offset + -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 + (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence + happened. The specific occurrence must be defined in the + description of any schema node defined using this type. When + the specific occurrence occurred prior to the last time the + associated timeticks attribute was zero, then the timestamp + value is zero. Note that this requires all timestamp values + to be reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + /*** collection of string types ***/ + + typedef hex-string { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "A hexadecimal string with octets represented as hex digits + separated by colons. The canonical representation uses + lowercase characters."; + } + + typedef uuid { + type string { + pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' + + '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + } + description + "A Universally Unique IDentifier in the string representation + defined in RFC 4122. The canonical representation uses + lowercase characters. + + The following is an example of a UUID in string representation: + f81d4fae-7dec-11d0-a765-00a0c91e6bf6 + "; + reference + "RFC 4122: A Universally Unique IDentifier (UUID) URN + Namespace"; + } + + typedef dotted-quad { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; + } + description + "An unsigned 32-bit number expressed in the dotted-quad + notation, i.e., four octets written as decimal numbers + and separated with the '.' (full stop) character."; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/sal-remote@2014-01-14.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/sal-remote@2014-01-14.yang new file mode 100644 index 0000000..d12e252 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/sal-remote@2014-01-14.yang @@ -0,0 +1,98 @@ +module sal-remote { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; + prefix "sal-remote"; + + + organization "Cisco Systems, Inc."; + contact "Martin Bobak <mbobak@cisco.com>"; + + description + "This module contains the definition of methods related to + sal remote model. + + Copyright (c)2013 Cisco Systems, Inc. All rights reserved. + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2014-01-14" { + description + "Initial revision"; + } + + + typedef q-name { + type string; + reference + "http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#QName"; + } + + rpc create-data-change-event-subscription { + input { + leaf path { + type instance-identifier; + description "Subtree path. "; + } + } + output { + leaf stream-name { + type string; + description "Notification stream name."; + } + } + } + + notification data-changed-notification { + description "Data change notification."; + list data-change-event { + key path; + leaf path { + type instance-identifier; + } + leaf store { + type enumeration { + enum config; + enum operation; + } + } + leaf operation { + type enumeration { + enum created; + enum updated; + enum deleted; + } + } + anyxml data{ + description "DataObject "; + } + } + } + + rpc create-notification-stream { + input { + leaf-list notifications { + type q-name; + description "Notification QNames"; + } + } + output { + leaf notification-stream-identifier { + type string; + description "Unique notification stream identifier, in which notifications will be propagated"; + } + } + } + + rpc begin-transaction{ + output{ + anyxml data-modification-transaction{ + description "DataModificationTransaction xml"; + } + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/simple-nodes.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/simple-nodes.yang new file mode 100644 index 0000000..6c16254 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/simple-nodes.yang @@ -0,0 +1,80 @@ +module simple-nodes { + yang-version 1; + namespace "urn:opendaylight:simple-nodes"; + prefix "sn"; + + description + "test file containing yang data nodes"; + + revision "2013-07-30" { + description + "Initial revision."; + reference "will be defined"; + } + + container users { + leaf user { + type string; + } + + leaf group { + type string; + } + } + + list user { + key "name class"; + leaf name { + type string; + } + leaf full-name { + type string; + } + leaf class { + type string; + } + } + + list userWithoutClass { + key "name"; + leaf name { + type string; + } + leaf full-name { + type string; + } + container inner-container { + leaf inner-list { + type uint16; + } + } + } + + container food { + choice snack { + case sports-arena { + leaf pretzel { + type string; + } + leaf beer { + type string; + } + container nonalcoholic { + leaf beer { + type string; + } + } + } + case late-night { + leaf chocolate { + type enumeration { + enum dark; + enum milk; + enum first-available; + } + } + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/subscribe-to-notification.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/subscribe-to-notification.yang new file mode 100644 index 0000000..5fe7df7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/subscribe-to-notification.yang @@ -0,0 +1,18 @@ +module subscribe-to-notification { + + yang-version 1; + namespace "subscribe:to:notification"; + prefix "subs-to-notifi"; + + description + "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream"; + + revision "2016-10-28" { + } + + container "notifi"{ + leaf "location"{ + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/toaster.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/toaster.yang new file mode 100644 index 0000000..571ed0c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/full-versions/yangs/toaster.yang @@ -0,0 +1,197 @@ + module toaster { + + yang-version 1; + + namespace + "http://netconfcentral.org/ns/toaster"; + + prefix toast; + + organization "Netconf Central"; + + contact + "Andy Bierman <andy@netconfcentral.org>"; + + description + "YANG version of the TOASTER-MIB."; + + revision "2009-11-20" { + description + "Toaster module in progress."; + } + + + identity toast-type { + description + "Base for all bread types supported by the toaster. + New bread types not listed here nay be added in the + future."; + } + + identity white-bread { + base toast:toast-type; + description "White bread."; + } + + identity wheat-bread { + base toast-type; + description "Wheat bread."; + } + + identity wonder-bread { + base toast-type; + description "Wonder bread."; + } + + identity frozen-waffle { + base toast-type; + description "Frozen waffle."; + } + + identity frozen-bagel { + base toast-type; + description "Frozen bagel."; + } + + identity hash-brown { + base toast-type; + description "Hash browned potatos."; + } + + typedef DisplayString { + type string { + length "0 .. 255"; + } + description + "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION."; + reference + "RFC 2579, section 2."; + + } + + container toaster { + presence + "Indicates the toaster service is available"; + description + "Top-level container for all toaster database objects."; + leaf toasterManufacturer { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's manufacturer. For instance, + Microsoft Toaster."; + } + + leaf toasterModelNumber { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's model. For instance, + Radiant Automatic."; + } + + leaf toasterStatus { + type enumeration { + enum "up" { + value 1; + description + "The toaster knob position is up. + No toast is being made now."; + } + enum "down" { + value 2; + description + "The toaster knob position is down. + Toast is being made now."; + } + } + config false; + mandatory true; + description + "This variable indicates the current state of + the toaster."; + } + } // container toaster + + rpc make-toast { + description + "Make some toast. + The toastDone notification will be sent when + the toast is finished. + An 'in-use' error will be returned if toast + is already being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + input { + leaf toasterDoneness { + type uint32 { + range "1 .. 10"; + } + default '5'; + description + "This variable controls how well-done is the + ensuing toast. It should be on a scale of 1 to 10. + Toast made at 10 generally is considered unfit + for human consumption; toast made at 1 is warmed + lightly."; + } + + leaf toasterToastType { + type identityref { + base toast:toast-type; + } + default 'wheat-bread'; + description + "This variable informs the toaster of the type of + material that is being toasted. The toaster + uses this information, combined with + toasterDoneness, to compute for how + long the material must be toasted to achieve + the required doneness."; + } + } + } // rpc make-toast + + rpc testOutput { + output { + leaf textOut { + type string; + } + } + } + + rpc cancel-toast { + description + "Stop making toast, if any is being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + } // rpc cancel-toast + + notification toastDone { + description + "Indicates that the toast in progress has completed."; + leaf toastStatus { + type enumeration { + enum "done" { + value 0; + description "The toast is done."; + } + enum "cancelled" { + value 1; + description + "The toast was cancelled."; + } + enum "error" { + value 2; + description + "The toaster service was disabled or + the toaster is broken."; + } + } + description + "Indicates the final toast status"; + } + } // notification toastDone + } // module toaster diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/iid-value.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/iid-value.yang new file mode 100644 index 0000000..4e536df --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/iid-value.yang @@ -0,0 +1,23 @@ +module iid-value-module { + namespace "iid:value:module"; + + prefix "iidvm"; + revision 2016-09-12 { + } + + container cont-iid { + list iid-list{ + key "iid-leaf"; + leaf iid-leaf{ + type instance-identifier; + } + } + + list values-iid{ + key "value-iid"; + leaf value-iid{ + type string; + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json new file mode 100644 index 0000000..1483920 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnContainer.json @@ -0,0 +1,43 @@ +{ + "ietf-yang-patch:yang-patch" : { + "patch-id" : "Test merge operation", + "comment" : "This is test patch for merge operation on container", + "edit" : [ + { + "edit-id": "edit1", + "operation": "create", + "target": "/", + "value": { + "patch-cont": { + "my-list1": [ + { + "name": "my-list1 - A", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + }, + { + "name": "my-list1 - B", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + } + ] + } + } + }, + { + "edit-id": "edit2", + "operation": "merge", + "target": "/", + "value": { + "patch-cont": { + "my-list1": { + "name": "my-list1 - Merged", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + } + } + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnList.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnList.json new file mode 100644 index 0000000..3b809e0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHMergeOperationOnList.json @@ -0,0 +1,32 @@ +{ + "ietf-yang-patch:yang-patch" : { + "patch-id" : "Test merge operation", + "comment" : "This is test patch for merge operation on list", + "edit" : [ + { + "edit-id": "edit1", + "operation": "replace", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "value": { + "my-list2": { + "name": "my-leaf20", + "my-leaf21": "I am leaf21-0", + "my-leaf22": "I am leaf22-0" + } + } + }, + { + "edit-id": "edit2", + "operation": "merge", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf21']", + "value": { + "my-list2": { + "name": "my-leaf21", + "my-leaf21": "I am leaf21-1", + "my-leaf22": "I am leaf22-1" + } + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHSimpleLeafValue.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHSimpleLeafValue.json new file mode 100644 index 0000000..4a109ef --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHSimpleLeafValue.json @@ -0,0 +1,17 @@ +{ + "ietf-yang-patch:yang-patch" : { + + "patch-id" : "test-patch", + "comment" : "this is test patch for simple leaf value", + "edit" : [ + { + "edit-id": "edit1", + "operation": "replace", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']/instance-identifier-patch-module:name", + "value": { + "name": "my-leaf20" + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdata.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdata.json new file mode 100644 index 0000000..e027a76 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdata.json @@ -0,0 +1,35 @@ +{ + "ietf-yang-patch:yang-patch" : { + + "patch-id" : "test-patch", + "comment" : "this is test patch", + "edit" : [ + { + "edit-id": "edit1", + "operation": "replace", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "value": { + "my-list2": { + "name": "my-leaf20", + "my-leaf21": "I am leaf21-0", + "my-leaf22": "I am leaf22-0" + } + } + }, + + { + "edit-id": "edit2", + "operation": "replace", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "value": { + "my-list2": { + "name": "my-leaf20", + "my-leaf21": "I am leaf21-1", + "my-leaf22": "I am leaf22-1", + "my-leaf-list": ["listelement"] + } + } + } + ] + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json new file mode 100644 index 0000000..1b170c7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCompleteTargetInURI.json @@ -0,0 +1,44 @@ +{ + "ietf-yang-patch:yang-patch" : { + + "patch-id" : "test-patch", + "comment" : "Test to create and replace data in container directly using / sign as a target", + "edit" : [ + { + "edit-id": "edit1", + "operation": "create", + "target": "/", + "value": { + "patch-cont": { + "my-list1": [ + { + "name": "my-list1 - A", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + }, + { + "name": "my-list1 - B", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + } + ] + } + } + }, + { + "edit-id": "edit2", + "operation": "replace", + "target": "/", + "value": { + "patch-cont": { + "my-list1": { + "name": "my-list1 - Replacing", + "my-leaf11": "I am leaf11-0", + "my-leaf12": "I am leaf12-1" + } + } + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json new file mode 100644 index 0000000..4455038 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataCreateAndDelete.json @@ -0,0 +1,31 @@ +{ + "ietf-yang-patch:yang-patch" : { + "patch-id" : "test-patch", + "comment" : "this is test patch", + "edit" : [ + { + "edit-id": "edit1", + "value": { + "my-list2": [ + { + "name": "my-leaf20", + "my-leaf21": "I am leaf20" + }, + { + "name": "my-leaf21", + "my-leaf21": "I am leaf21-1", + "my-leaf22": "I am leaf21-2" + } + ] + }, + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "operation": "create" + }, + { + "edit-id": "edit2", + "operation": "delete", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']" + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueMissing.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueMissing.json new file mode 100644 index 0000000..eaf1b37 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueMissing.json @@ -0,0 +1,13 @@ +{ + "ietf-yang-patch:yang-patch" : { + "patch-id" : "test-patch", + "comment" : "this is test patch", + "edit" : [ + { + "edit-id": "edit1", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "operation": "create" + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueNotSupported.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueNotSupported.json new file mode 100644 index 0000000..1ad52fb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsonPATCHdataValueNotSupported.json @@ -0,0 +1,20 @@ +{ + "ietf-yang-patch:yang-patch" : { + "patch-id" : "test-patch", + "comment" : "this is test patch", + "edit" : [ + { + "edit-id": "edit2", + "operation": "delete", + "target": "/instance-identifier-patch-module:my-list2[instance-identifier-patch-module:name='my-leaf20']", + "value": { + "my-list2": [ + { + "name": "my-leaf20" + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_choice_container.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_choice_container.json new file mode 100644 index 0000000..e64e00e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_choice_container.json @@ -0,0 +1,5 @@ +{ + "augment-module:case-choice-case-container1": { + "augment-module:case-choice-case-leaf1": "stryng" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_container.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_container.json new file mode 100644 index 0000000..e2532f2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_augment_container.json @@ -0,0 +1,5 @@ +{ + "augment-module:cont-augment": { + "augment-module:leaf1": "stryng" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_sub_container.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_sub_container.json new file mode 100644 index 0000000..22a1d15 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/json_sub_container.json @@ -0,0 +1,5 @@ +{ + "instance-identifier-module:cont1": { + "augment-module-leaf-list:lf11" : "/instance-identifier-module:cont/instance-identifier-module:cont1/augment-module-leaf-list:lflst11[.=\"lflst11_1\"]" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata.json new file mode 100644 index 0000000..f616a8a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata.json @@ -0,0 +1,14 @@ +{ + "instance-identifier-module:cont": { + "cont1": { + "augment-module:lst11": [ + { + "keyvalue111":"value1", + "keyvalue112":"value2", + "augment-augment-module:lf111":"/instance-identifier-module:cont/instance-identifier-module:cont1/augment-module:lst11[augment-module:keyvalue111=\"value1\"][augment-module:keyvalue112=\"value2\"]/augment-augment-module:lf112", + "augment-augment-module:lf112":"lf112 value" + } + ] + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata_leaf_list.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata_leaf_list.json new file mode 100644 index 0000000..63be4b9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/json/jsondata_leaf_list.json @@ -0,0 +1,7 @@ +{ + "instance-identifier-module:cont": { + "cont1": { + "augment-module-leaf-list:lf11" : "/instance-identifier-module:cont/instance-identifier-module:cont1/augment-module-leaf-list:lflst11[.=\"lflst11_1\"]" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/bug7933.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/bug7933.xml new file mode 100644 index 0000000..aa0d79e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/bug7933.xml @@ -0,0 +1,9 @@ +<!-- + ~ Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> + +<cont1 xmlns="instance:identifier:module"/> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml new file mode 100644 index 0000000..6523345 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindBarContainer.xml @@ -0,0 +1,10 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> + +<foo-bar-container xmlns="bar:module"> +</foo-bar-container>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml new file mode 100644 index 0000000..93aeff8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlDataFindFooContainer.xml @@ -0,0 +1,10 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> + +<foo-bar-container xmlns="foo:module"> +</foo-bar-container>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml new file mode 100644 index 0000000..d7d3a6b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdata.xml @@ -0,0 +1,28 @@ +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>test-patch</patch-id> + <comment>this is test patch</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>create</operation> + <target>/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf20</name> + <my-leaf21>I am leaf21-0</my-leaf21> + <my-leaf22>I am leaf22-0</my-leaf22> + </my-list2> + </value> + </edit> + <edit> + <edit-id>edit2</edit-id> + <operation>create</operation> + <target>/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf21</name> + <my-leaf21>I am leaf21-1</my-leaf21> + <my-leaf22>I am leaf22-1</my-leaf22> + </my-list2> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml new file mode 100644 index 0000000..6e84c47 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataAbsoluteTargetPath.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>test-patch</patch-id> + <comment>Test patch with absolute target path</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>create</operation> + <target>/instance-identifier-patch-module:patch-cont/my-list1/leaf1/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf20</name> + <my-leaf21>I am leaf21-0</my-leaf21> + <my-leaf22>I am leaf22-0</my-leaf22> + </my-list2> + </value> + </edit> + <edit> + <edit-id>edit2</edit-id> + <operation>create</operation> + <target>/instance-identifier-patch-module:patch-cont/my-list1/leaf1/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf21</name> + <my-leaf21>I am leaf21-1</my-leaf21> + <my-leaf22>I am leaf22-1</my-leaf22> + </my-list2> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml new file mode 100644 index 0000000..23d2ce0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataCompleteTargetInURI.xml @@ -0,0 +1,44 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>test-patch</patch-id> + <comment>Test to create and replace data in container directly using / sign as a target</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>create</operation> + <target>/</target> + <value> + <patch-cont xmlns="instance:identifier:patch:module"> + <my-list1> + <name>my-list1 - A</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + <my-list1> + <name>my-list1 - B</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + </patch-cont> + </value> + </edit> + <edit> + <edit-id>edit2</edit-id> + <operation>replace</operation> + <target>/</target> + <value> + <patch-cont xmlns="instance:identifier:patch:module"> + <my-list1> + <name>my-list1 - Replacing</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + </patch-cont> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml new file mode 100644 index 0000000..afa35bd --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnContainer.xml @@ -0,0 +1,44 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>Test merge operation</patch-id> + <comment>This is test patch for merge operation on container</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>create</operation> + <target>/</target> + <value> + <patch-cont xmlns="instance:identifier:patch:module"> + <my-list1> + <name>my-list1 - A</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + <my-list1> + <name>my-list1 - B</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + </patch-cont> + </value> + </edit> + <edit> + <edit-id>edit2</edit-id> + <operation>merge</operation> + <target>/</target> + <value> + <patch-cont xmlns="instance:identifier:patch:module"> + <my-list1> + <name>my-list1 - Merged</name> + <my-leaf11>I am leaf11-0</my-leaf11> + <my-leaf12>I am leaf12-1</my-leaf12> + </my-list1> + </patch-cont> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml new file mode 100644 index 0000000..ad13041 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataMergeOperationOnList.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>Test merge operation</patch-id> + <comment>This is test patch for merge operation on list</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>replace</operation> + <target>/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf20</name> + <my-leaf21>I am leaf21-0</my-leaf21> + <my-leaf22>I am leaf22-0</my-leaf22> + </my-list2> + </value> + </edit> + <edit> + <edit-id>edit2</edit-id> + <operation>merge</operation> + <target>/my-list2</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf21</name> + <my-leaf21>I am leaf21-1</my-leaf21> + <my-leaf22>I am leaf22-1</my-leaf22> + </my-list2> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueMissing.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueMissing.xml new file mode 100644 index 0000000..eeec5ad --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueMissing.xml @@ -0,0 +1,16 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>test-patch</patch-id> + <comment>Test patch with missing value node for create operation</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>create</operation> + <target>/my-list2</target> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml new file mode 100644 index 0000000..8817094 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmlPATCHdataValueNotSupported.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + ~ + ~ This program and the accompanying materials are made available under the + ~ terms of the Eclipse Public License v1.0 which accompanies this distribution, + ~ and is available at http://www.eclipse.org/legal/epl-v10.html + --> +<yang-patch xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch"> + <patch-id>test-patch</patch-id> + <comment>Test patch with not allowed value node for delete operation</comment> + <edit> + <edit-id>edit1</edit-id> + <operation>delete</operation> + <target>/my-list2/my-leaf21</target> + <value> + <my-list2 xmlns="instance:identifier:patch:module"> + <name>my-leaf20</name> + <my-leaf21>I am leaf21-0</my-leaf21> + <my-leaf22>I am leaf22-0</my-leaf22> + </my-list2> + </value> + </edit> +</yang-patch>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_choice_container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_choice_container.xml new file mode 100644 index 0000000..d73b300 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_choice_container.xml @@ -0,0 +1,3 @@ +<case-choice-case-container1 xmlns="augment:module"> + <case-choice-case-leaf1>stryng</case-choice-case-leaf1> +</case-choice-case-container1>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_container.xml new file mode 100644 index 0000000..6b35e9e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_augment_container.xml @@ -0,0 +1,3 @@ +<cont-augment xmlns="augment:module"> + <leaf1>stryng</leaf1> +</cont-augment>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_sub_container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_sub_container.xml new file mode 100644 index 0000000..f76e08d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xml_sub_container.xml @@ -0,0 +1,6 @@ +<cont1 xmlns="instance:identifier:module"> + <lflst11 xmlns="augment:module:leaf:list">lflst11_1</lflst11> + <lflst11 xmlns="augment:module:leaf:list">lflst11_2</lflst11> + <lflst11 xmlns="augment:module:leaf:list">lflst11_3</lflst11> + <lf11 xmlns:a="instance:identifier:module" xmlns:b="augment:module:leaf:list" xmlns="augment:module:leaf:list">/a:cont/a:cont1/b:lflst11[.="lflst11_1"]</lf11> +</cont1>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata.xml new file mode 100644 index 0000000..bd558fb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata.xml @@ -0,0 +1,10 @@ +<cont xmlns="instance:identifier:module"> + <cont1> + <lst11 xmlns="augment:module" xmlns:c="augment:augment:module"> + <keyvalue111>value1</keyvalue111> + <keyvalue112>value2</keyvalue112> + <lf111 xmlns="augment:augment:module" xmlns:a="instance:identifier:module" xmlns:b="augment:module" >/a:cont/a:cont1/b:lst11[b:keyvalue111="value1"][b:keyvalue112="value2"]/c:lf112</lf111> + <lf112 xmlns="augment:augment:module">lf112 value</lf112> + </lst11> + </cont1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata_leaf_list.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata_leaf_list.xml new file mode 100644 index 0000000..00d58c2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/xml/xmldata_leaf_list.xml @@ -0,0 +1,8 @@ +<cont xmlns="instance:identifier:module"> + <cont1> + <lflst11 xmlns="augment:module:leaf:list">lflst11_1</lflst11> + <lflst11 xmlns="augment:module:leaf:list">lflst11_2</lflst11> + <lflst11 xmlns="augment:module:leaf:list">lflst11_3</lflst11> + <lf11 xmlns:a="instance:identifier:module" xmlns:b="augment:module:leaf:list" xmlns="augment:module:leaf:list">/a:cont/a:cont1/b:lflst11[.="lflst11_1"]</lf11> + </cont1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-augment-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-augment-module.yang new file mode 100644 index 0000000..546111c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-augment-module.yang @@ -0,0 +1,21 @@ +module augment-augment-module { + namespace "augment:augment:module"; + + prefix "aamodule"; + + import augment-module {prefix amodule; revision-date 2014-01-17;} + import instance-identifier-module {prefix imodule; revision-date 2014-01-17;} + + revision 2014-01-17 { + } + + augment "/imodule:cont/imodule:cont1/amodule:lst11" { + leaf lf111 { + type instance-identifier; + } + leaf lf112 { + type string; + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module-leaf-list.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module-leaf-list.yang new file mode 100644 index 0000000..54c305b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module-leaf-list.yang @@ -0,0 +1,20 @@ +module augment-module-leaf-list { + namespace "augment:module:leaf:list"; + + prefix "amodulelflst"; + + import instance-identifier-module {prefix imodule; revision-date 2014-01-17;} + + revision 2014-01-27 { + } + + augment "/imodule:cont/imodule:cont1" { + leaf-list lflst11 { + type string; + } + leaf lf11 { + type instance-identifier; + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module.yang new file mode 100644 index 0000000..c918ef9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/augment-module.yang @@ -0,0 +1,63 @@ +module augment-module { + namespace "augment:module"; + + prefix "amodule"; + + import instance-identifier-module {prefix imodule; revision-date 2014-01-17;} + + revision 2014-01-17 { + } + + augment "/imodule:cont/imodule:cont1" { + list lst11 { + key "keyvalue111 keyvalue112"; + leaf keyvalue111 { + type string; + } + leaf keyvalue112 { + type string; + } + } + } + + augment "/imodule:cont" { + container cont-augment { + leaf leaf1 { + type string; + } + } + } + + augment "/imodule:cont" { + choice augment-choice1 { + case case1 { + container case-container1 { + leaf case-leaf1 { + type string; + } + } + } + + case case2 { + container case-container2 { + leaf case-leaf2 { + type string; + } + } + } + } + } + + augment "/imodule:cont/augment-choice1/case1" { + choice augment-choice2 { + case case11 { + container case-choice-case-container1 { + leaf case-choice-case-leaf1 { + type string; + } + } + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/bar-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/bar-module.yang new file mode 100644 index 0000000..90de085 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/bar-module.yang @@ -0,0 +1,11 @@ +module bar-module { + namespace "bar:module"; + + prefix "bar-module"; + revision 2016-09-29 { + } + + /* This container has the same name as container in foo-module */ + container foo-bar-container { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/foo-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/foo-module.yang new file mode 100644 index 0000000..16b8e7f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/foo-module.yang @@ -0,0 +1,11 @@ +module foo-module { + namespace "foo:module"; + + prefix "foo-module"; + revision 2016-09-29 { + } + + /* This container has the same name as container in bar-module */ + container foo-bar-container { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang new file mode 100644 index 0000000..7a95a5f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-module.yang @@ -0,0 +1,13 @@ +module instance-identifier-module { + namespace "instance:identifier:module"; + + prefix "iimodule"; + revision 2014-01-17 { + } + + container cont { + container cont1 { + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang new file mode 100644 index 0000000..02c1c2e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/instanceidentifier/yang/instance-identifier-patch-module.yang @@ -0,0 +1,47 @@ +module instance-identifier-patch-module { + namespace "instance:identifier:patch:module"; + + prefix "iipmodule"; + revision 2015-11-21 { + } + + container patch-cont { + list my-list1 { + + description "PATCH /restconf/config/instance-identifier-patch-module:patch-cont/my-list1/leaf1"; + + key name; + + leaf name { + type string; + } + + leaf my-leaf11 { + type string; + } + + leaf my-leaf12 { + type string; + } + + list my-list2 { + key name; + + leaf name { + type string; + } + + leaf my-leaf21 { + type string; + } + + leaf my-leaf22 { + type string; + } + leaf-list my-leaf-list { + type string; + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/invoke-rpc-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/invoke-rpc-module.yang new file mode 100644 index 0000000..e744818 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/invoke-rpc-module.yang @@ -0,0 +1,30 @@ +module invoke-rpc-module { + namespace "invoke:rpc:module"; + + prefix "inrpcmod"; + + revision 2013-12-03 { + + } + + rpc rpc-test { + input { + container cont { + leaf lf { + type string; + } + } + } + output { + container cont-out { + leaf lf-out { + type string; + } + } + } + } + + rpc rpc-noop { + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-input.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-input.json new file mode 100644 index 0000000..2ba5f27 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-input.json @@ -0,0 +1,7 @@ +{ + "invoke-rpc-module:input" : { + "cont" : { + "lf" : "lf-test" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-output.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-output.json new file mode 100644 index 0000000..107f4c7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/json/rpc-output.json @@ -0,0 +1,7 @@ +{ + "invoke-rpc-module:output" : { + "cont-out" : { + "lf-out" : "lf-test" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-input.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-input.xml new file mode 100644 index 0000000..48fb557 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-input.xml @@ -0,0 +1,5 @@ +<input xmlns="invoke:rpc:module"> + <cont> + <lf>lf-test</lf> + </cont> +</input>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-output.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-output.xml new file mode 100644 index 0000000..3b11eb8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/invoke-rpc/xml/rpc-output.xml @@ -0,0 +1,5 @@ +<output xmlns="invoke:rpc:module"> + <cont-out> + <lf-out>lf-test</lf-out> + </cont-out> +</output>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/array-with-null.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/array-with-null.json new file mode 100644 index 0000000..995b3c6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/array-with-null.json @@ -0,0 +1,5 @@ +{ + "array-with-null-yang:cont": { + "lf":null + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data.json new file mode 100644 index 0000000..aa7142d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data.json @@ -0,0 +1,6 @@ +{ + "array-with-null-yang:cont": { + "lflst1":[], + "lflst2":[45] + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data1.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data1.json new file mode 100644 index 0000000..b886fc9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/empty-data1.json @@ -0,0 +1,5 @@ +{ + "cont": { + "lf": + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identity-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identity-module.yang new file mode 100644 index 0000000..09a34c5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identity-module.yang @@ -0,0 +1,10 @@ +module identity-module { + namespace "identity:module"; + + prefix "idemod"; + revision 2013-12-02 { + } + + identity iden { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identityref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identityref-module.yang new file mode 100644 index 0000000..80ec396 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/identityref-module.yang @@ -0,0 +1,39 @@ +module identityref-module { + namespace "identityref:module"; + + prefix "iderefmod"; + + import identity-module {prefix idemo; revision-date 2013-12-02; } + + revision 2013-12-02 { + } + + identity iden_local { + } + + container cont { + container cont1 { + leaf lf11 { + type identityref { + base "idemo:iden"; + } + } + leaf lf12 { + type identityref { + base "iden_local"; + } + } + leaf lf13 { + type identityref { + base "iden_local"; + } + } + leaf lf14 { + type identityref { + base "iden_local"; + } + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/json/data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/json/data.json new file mode 100644 index 0000000..88cd6b4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/identityref/json/data.json @@ -0,0 +1,10 @@ +{ + "identityref:cont":{ + "cont1":{ + "lf11":"identity-module:iden", + "lf12":"identityref-module:iden_local", + "identityref-module:lf13":"identityref-module:iden_local", + "identityref-module:lf14":"identity-module:iden" + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/invalid-uri-character-in-value.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/invalid-uri-character-in-value.json new file mode 100644 index 0000000..545f99b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/invalid-uri-character-in-value.json @@ -0,0 +1,6 @@ +{ + "array-with-null-yang:cont":{ + "lf1":"module<Name:value lf1", + "lf2":"module>Name:value lf2" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/augment-leafref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/augment-leafref-module.yang new file mode 100644 index 0000000..8a55e43 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/augment-leafref-module.yang @@ -0,0 +1,18 @@ +module augment-leafref-module { + namespace "augment:leafref:module"; + + prefix "auglfrfmo"; + revision 2014-12-16; + + typedef leafreftype { + type leafref { + path "/auglfrfmo:cont/auglfrfmo:lf3"; + } + } + + container cont { + leaf lf3 { + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/json/data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/json/data.json new file mode 100644 index 0000000..df5bcef --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/json/data.json @@ -0,0 +1,7 @@ +{ + "leafref-module:cont":{ + "lf1":121, + "lf2":121, + "lf4":"pcc://39.39.39.39" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/leafref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/leafref-module.yang new file mode 100644 index 0000000..9b124a0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/leafref/leafref-module.yang @@ -0,0 +1,24 @@ +module leafref-module { + namespace "leafref:module"; + + prefix "lfrfmo"; + + import augment-leafref-module {prefix augleafref; revision-date 2014-12-16;} + revision 2013-11-18 { + } + + container cont { + leaf lf1 { + type int32; + } + leaf lf2 { + type leafref { + path "/cont/lf1"; + } + } + leaf lf4 { + type augleafref:leafreftype; + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-items-in-list.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-items-in-list.json new file mode 100644 index 0000000..2131744 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-items-in-list.json @@ -0,0 +1,26 @@ +{ + "multiple-items-yang:lst":[ + { + "lst1": [ + { + "lf11":"lf11_1" + }, + { + "lflst11":[ + 45 + ] + }, + { + "cont11":{ + } + }, + { + "lst11":[ + { + } + ] + } + ] + } + ] +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-leaflist-items.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-leaflist-items.json new file mode 100644 index 0000000..c1f5d1e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/multiple-leaflist-items.json @@ -0,0 +1,5 @@ +{ + "simple-list-yang1:lst": { + "lflst1":[45,55,66] + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container-yang/simple-container.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container-yang/simple-container.yang new file mode 100644 index 0000000..493101c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container-yang/simple-container.yang @@ -0,0 +1,20 @@ +module simple-container-yang { + namespace "simple:container:yang"; + + prefix "smpdtp"; + revision 2013-11-12 { + } + + container cont { + container cont1 { + } + list lst1 { + } + leaf-list lflst1 { + type string; + } + leaf lf1 { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container.json new file mode 100644 index 0000000..28d244d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-container.json @@ -0,0 +1,15 @@ +{ + "simple-container-yang:cont":{ + "cont1":{ + }, + "lst1": [ + { + } + ], + "lflst1":[ + "lflst1_1", + "lflst1_2" + ], + "lf1":"lf1" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/1/simple-list1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/1/simple-list1.yang new file mode 100644 index 0000000..0ce8ea4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/1/simple-list1.yang @@ -0,0 +1,20 @@ +module simple-list-yang1 { + namespace "simple:list:yang1"; + + prefix "smplstyg"; + revision 2013-11-12 { + } + + list lst { + container cont1 { + } + list lst1 { + } + leaf-list lflst1 { + type string; + } + leaf lf1 { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/2/simple-list2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/2/simple-list2.yang new file mode 100644 index 0000000..0872a47 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/2/simple-list2.yang @@ -0,0 +1,20 @@ +module simple-list-yang2 { + namespace "simple:list:yang2"; + + prefix "smplstyg"; + revision 2013-11-12 { + } + + list lst { + container cont1 { + } + list lst1 { + } + leaf-list lflst1 { + type string; + } + leaf lf1 { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/3/multiple-items.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/3/multiple-items.yang new file mode 100644 index 0000000..dcc17d5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/3/multiple-items.yang @@ -0,0 +1,24 @@ +module multiple-items-yang { + namespace "multiple:items:yang"; + + prefix "mltitmsyg"; + revision 2013-11-12 { + } + + list lst { + list lst1 { + leaf lf11{ + type string; + } + leaf-list lflst11{ + type string; + } + container cont11{ + + } + list lst11{ + + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/4/array-with-null.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/4/array-with-null.yang new file mode 100644 index 0000000..869e406 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list-yang/4/array-with-null.yang @@ -0,0 +1,26 @@ +module array-with-null-yang { + namespace "array:with:null:yang"; + + prefix "arwnlyn"; + revision 2013-11-12 { + } + + container cont{ + leaf lf{ + type empty; + } + leaf lf1{ + type string; + } + leaf lf2{ + type string; + } + leaf-list lflst1{ + type empty; + } + + leaf-list lflst2{ + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list.json new file mode 100644 index 0000000..76a0190 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/simple-list.json @@ -0,0 +1,17 @@ +{ + "simple-list-yang1:lst":[ + { + "cont1":{ + }, + "lst1": [ + { + } + ], + "lflst1":[ + "lflst1_1", + "lflst1_2" + ], + "lf1":"lf1" + } + ] +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/unsupported-json-format.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/unsupported-json-format.json new file mode 100644 index 0000000..abc6267 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/unsupported-json-format.json @@ -0,0 +1 @@ +fffff
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level1.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level1.json new file mode 100644 index 0000000..88baef3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level1.json @@ -0,0 +1,8 @@ +{ + "wrong":[ + { + }, + { + } + ] +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level2.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level2.json new file mode 100644 index 0000000..c567273 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level2.json @@ -0,0 +1,9 @@ +{ + + "simple-list-yang1:lst": { + }, + "lst1":[ + { + } + ] +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level3.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level3.json new file mode 100644 index 0000000..7288969 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/json-to-nn/wrong-top-level3.json @@ -0,0 +1,3 @@ +{ + "lf":"hello" +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang new file mode 100644 index 0000000..abbd5d0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang @@ -0,0 +1,16 @@ +module augmented-jukebox { + + namespace "http://example.com/ns/augmented-jukebox"; + prefix "augmented-jbox"; + + revision "2016-05-05" { + description "Initial version."; + } + + import example-jukebox {prefix jbox; revision-date "2015-04-04";} + + augment "/jbox:jukebox" { + container augmented-library { + } + } + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/example-jukebox@2015-04-04.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/example-jukebox@2015-04-04.yang new file mode 100644 index 0000000..ddaa602 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/example-jukebox@2015-04-04.yang @@ -0,0 +1,243 @@ +module example-jukebox { + + namespace "http://example.com/ns/example-jukebox"; + prefix "jbox"; + + organization "Example, Inc."; + contact "support at example.com"; + description "Example Jukebox Data Model Module"; + revision "2015-04-04" { + description "Initial version."; + reference "example.com document 1-4673"; + } + + identity genre { + description "Base for all genre types"; + } + + // abbreviated list of genre classifications + identity alternative { + base genre; + description "Alternative music"; + } + identity blues { + base genre; + description "Blues music"; + } + identity country { + base genre; + description "Country music"; + } + identity jazz { + base genre; + description "Jazz music"; + } + identity pop { + base genre; + description "Pop music"; + } + identity rock { + base genre; + description "Rock music"; + } + + container jukebox { + presence + "An empty container indicates that the jukebox + service is available"; + + description + "Represents a jukebox resource, with a library, playlists, + and a play operation."; + + container library { + + description "Represents the jukebox library resource."; + + list artist { + key name; + + description + "Represents one artist resource within the + jukebox library resource."; + + leaf name { + type string { + length "1 .. max"; + } + description "The name of the artist."; + } + + list album { + key name; + + description + "Represents one album resource within one + artist resource, within the jukebox library."; + + leaf name { + type string { + length "1 .. max"; + } + description "The name of the album."; + } + + leaf genre { + type identityref { base genre; } + description + "The genre identifying the type of music on + the album."; + } + + leaf year { + type uint16 { + range "1900 .. max"; + } + description "The year the album was released"; + } + + container admin { + description + "Administrative information for the album."; + + leaf label { + type string; + description "The label that released the album."; + } + leaf catalogue-number { + type string; + description "The album's catalogue number."; + } + } + + list song { + key name; + + description + "Represents one song resource within one + album resource, within the jukebox library."; + + leaf name { + type string { + length "1 .. max"; + } + description "The name of the song"; + } + leaf location { + type string; + mandatory true; + description + "The file location string of the + media file for the song"; + } + leaf format { + type string; + description + "An identifier string for the media type + for the file associated with the + 'location' leaf for this entry."; + } + leaf length { + type uint32; + units "seconds"; + description + "The duration of this song in seconds."; + } + } // end list 'song' + } // end list 'album' + } // end list 'artist' + + leaf artist-count { + type uint32; + units "songs"; + config false; + description "Number of artists in the library"; + } + leaf album-count { + type uint32; + units "albums"; + config false; + description "Number of albums in the library"; + } + leaf song-count { + type uint32; + units "songs"; + config false; + description "Number of songs in the library"; + } + } // end library + + list playlist { + key name; + + description + "Example configuration data resource"; + + leaf name { + type string; + description + "The name of the playlist."; + } + leaf description { + type string; + description + "A comment describing the playlist."; + } + + list song { + key index; + ordered-by user; + + description + "Example nested configuration data resource"; + + leaf index { // not really needed + type uint32; + description + "An arbitrary integer index for this playlist song."; + } + leaf id { + type leafref { + path "/jbox:jukebox/jbox:library/jbox:artist/" + + "jbox:album/jbox:song/jbox:name"; + } + mandatory true; + description + "Song identifier. Must identify an instance of + /jukebox/library/artist/album/song/name."; + } + } + } + + container player { + description + "Represents the jukebox player resource."; + + leaf gap { + type decimal64 { + fraction-digits 1; + range "0.0 .. 2.0"; + } + units "tenths of seconds"; + description "Time gap between each song"; + } + } + } + + rpc play { + description "Control function for the jukebox player"; + input { + leaf playlist { + type string; + mandatory true; + description "playlist name"; + } + + leaf song-number { + type uint32; + mandatory true; + description "Song number in playlist to play"; + } + } + } + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/jukebox/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/json/jsondata.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/json/jsondata.json new file mode 100644 index 0000000..f4a435e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/json/jsondata.json @@ -0,0 +1,8 @@ +{ + "leafref-module:cont" : { + "lf4" : "/referenced-module:cont/referenced-module:lf1", + "lf2" : "/leafref-module:cont/leafref-module:lf1", + "lf3" : "/leafref-module:cont/leafref-module:lf2", + "lf5" : "/leafref-module:cont/leafref-module:lf3" + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/xml/xmldata.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/xml/xmldata.xml new file mode 100644 index 0000000..1b5ce83 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/xml/xmldata.xml @@ -0,0 +1,6 @@ +<cont xmlnsa="leafref:module"> + <lf4 xmlns:nsa="referenced:module">/nsa:cont/nsa:lf1</lf4> + <lf2 xmlns:nsa="leafref:module">/nsa:cont/nsa:lf1</lf2> + <lf3 xmlns:ns="leafref:module">/ns:cont/ns:lf2</lf3> + <lf5 xmlns:nsa="leafref:module">/nsa:cont/nsa:lf3</lf5> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/leafref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/leafref-module.yang new file mode 100644 index 0000000..9675b79 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/leafref-module.yang @@ -0,0 +1,54 @@ +module leafref-module { + namespace "leafref:module"; + + + prefix "lfrfmodule"; + + import referenced-module { prefix refmod; revision-date 2014-04-17;} + + + revision 2014-04-17 { + } + + + container cont { + leaf lf1 { + type instance-identifier; + } + + leaf lf2 { + type leafref { + path "../lf1"; + } + } + + leaf lf3 { + type leafref { + path "/refmod:cont/refmod:lf1"; + } + } + + leaf lf4 { + type leafref { + path "/cont/lf1"; + } + } + + leaf lf5 { + type leafref { + path "../lf1"; + } + } + + list lst-with-lfref-key { + key "lfref-key"; + + leaf lfref-key { + type leafref { + path "/refmod:cont/refmod:id-ref"; + } + } + } + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/referenced-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/referenced-module.yang new file mode 100644 index 0000000..05dd123 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/leafref/yang/referenced-module.yang @@ -0,0 +1,23 @@ +module referenced-module { + namespace "referenced:module"; + + prefix "refmodule"; + revision 2014-04-17 { + } + + container cont { + leaf lf1 { + type instance-identifier; + } + + leaf id-ref { + type leafref { + path "../../id"; + } + } + } + + leaf id { + type string; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-create.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-create.json new file mode 100644 index 0000000..bc1cf52 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-create.json @@ -0,0 +1,55 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "data": { + "my-leaf11": { + "content": "Jed", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11" + }, + { + "data": { + "name": { + "content": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + }, + { + "data": { + "patch-cont": { + "my-list1": { + "my-leaf11": "Jed", + "name": "Althea" + }, + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont" + }, + { + "data": { + "my-list1": { + "my-leaf11": "Jed", + "name": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-17T13:32:03.586+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-del.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-del.json new file mode 100644 index 0000000..dbadab2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-del.json @@ -0,0 +1,31 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-17T14:18:53.404+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-create.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-create.json new file mode 100644 index 0000000..52be56a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-create.json @@ -0,0 +1,31 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "data": { + "my-leaf11": { + "content": "Jed", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11" + }, + { + "data": { + "name": { + "content": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-17T11:23:10.323+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-del.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-del.json new file mode 100644 index 0000000..5d9e9f1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-del.json @@ -0,0 +1,23 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + }, + { + "operation": "deleted", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-18T15:30:16.099+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-update.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-update.json new file mode 100644 index 0000000..ffef660 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-leaves-update.json @@ -0,0 +1,31 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "data": { + "my-leaf12": { + "content": "Bertha", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12" + }, + { + "data": { + "name": { + "content": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "updated", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-18T14:20:54.82+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-update.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-update.json new file mode 100644 index 0000000..b2957ea --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-update.json @@ -0,0 +1,57 @@ +{ + "notification": { + "data-changed-notification": { + "data-change-event": [ + { + "data": { + "patch-cont": { + "my-list1": { + "my-leaf11": "Jed", + "my-leaf12": "Bertha", + "name": "Althea" + }, + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "updated", + "path": "/instance-identifier-patch-module:patch-cont" + }, + { + "data": { + "my-list1": { + "my-leaf11": "Jed", + "my-leaf12": "Bertha", + "name": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "updated", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']" + }, + { + "data": { + "my-leaf12": { + "content": "Bertha", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "created", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf12" + }, + { + "data": { + "name": { + "content": "Althea", + "xmlns": "instance:identifier:patch:module" + } + }, + "operation": "updated", + "path": "/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:name" + } + ], + "xmlns": "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote" + }, + "eventTime": "2017-09-18T15:52:25.213+03:00", + "xmlns": "urn:ietf:params:xml:ns:netconf:notification:1.0" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-create.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-create.json new file mode 100644 index 0000000..6e4dadc --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-create.json @@ -0,0 +1,13 @@ +{ + "notification":{ + "xmlns":"urn:ietf:params:xml:ns:netconf:notification:1.0", + "data-changed-notification":{ + "xmlns":"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", + "data-change-event":{ + "path":"/instance-identifier-patch-module:patch-cont", + "operation":"created" + } + }, + "eventTime":"2020-05-31T18:45:05.132101+05:30" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-del.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-del.json new file mode 100644 index 0000000..dc3f739 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-del.json @@ -0,0 +1,13 @@ +{ + "notification":{ + "xmlns":"urn:ietf:params:xml:ns:netconf:notification:1.0", + "data-changed-notification":{ + "xmlns":"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", + "data-change-event":{ + "path":"/instance-identifier-patch-module:patch-cont", + "operation":"deleted" + } + }, + "eventTime":"2020-05-31T18:45:05.132101+05:30" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-update.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-update.json new file mode 100644 index 0000000..c22c956 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/listener-adapter-test/notif-without-data-update.json @@ -0,0 +1,13 @@ +{ + "notification":{ + "xmlns":"urn:ietf:params:xml:ns:netconf:notification:1.0", + "data-changed-notification":{ + "xmlns":"urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", + "data-change-event":{ + "path":"/instance-identifier-patch-module:patch-cont", + "operation":"updated" + } + }, + "eventTime":"2020-05-31T18:45:05.132101+05:30" + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/iana-if-type.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/iana-if-type.yang new file mode 100644 index 0000000..7bd0003 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/iana-if-type.yang @@ -0,0 +1,1517 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org"; + description + "This YANG module defines the iana-if-type typedef, which + contains YANG definitions for IANA-registered interface types. + + This YANG module is maintained by IANA, and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: IANA Interface Type YANG Module"; + } + + typedef iana-if-type { + type enumeration { + enum "other" { + value 1; + description + "None of the following"; + } + enum "regular1822" { + value 2; + } + enum "hdh1822" { + value 3; + } + enum "ddnX25" { + value 4; + } + enum "rfc877x25" { + value 5; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + enum "ethernetCsmacd" { + value 6; + description + "For all ethernet-like interfaces, regardless of speed, + as per RFC3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88023Csmacd" { + value 7; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88024TokenBus" { + value 8; + } + enum "iso88025TokenRing" { + value 9; + } + enum "iso88026Man" { + value 10; + } + enum "starLan" { + value 11; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "proteon10Mbit" { + value 12; + } + enum "proteon80Mbit" { + value 13; + } + enum "hyperchannel" { + value 14; + } + enum "fddi" { + value 15; + reference + "RFC 1512 - FDDI Management Information Base"; + } + enum "lapb" { + value 16; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + enum "sdlc" { + value 17; + } + enum "ds1" { + value 18; + description + "DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "e1" { + value 19; + status obsolete; + description + "Obsolete see DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "basicISDN" { + value 20; + description + "see also RFC2127"; + } + enum "primaryISDN" { + value 21; + } + enum "propPointToPointSerial" { + value 22; + description + "proprietary serial"; + } + enum "ppp" { + value 23; + } + enum "softwareLoopback" { + value 24; + } + enum "eon" { + value 25; + description + "CLNP over IP"; + } + enum "ethernet3Mbit" { + value 26; + } + enum "nsip" { + value 27; + description + "XNS over IP"; + } + enum "slip" { + value 28; + description + "generic SLIP"; + } + enum "ultra" { + value 29; + description + "ULTRA technologies"; + } + enum "ds3" { + value 30; + description + "DS3-MIB"; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + enum "sip" { + value 31; + description + "SMDS, coffee"; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + enum "frameRelay" { + value 32; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + enum "rs232" { + value 33; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + enum "para" { + value 34; + description + "parallel-port"; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + enum "arcnet" { + value 35; + description + "arcnet"; + } + enum "arcnetPlus" { + value 36; + description + "arcnet plus"; + } + enum "atm" { + value 37; + description + "ATM cells"; + } + enum "miox25" { + value 38; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + enum "sonet" { + value 39; + description + "SONET or SDH"; + } + enum "x25ple" { + value 40; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + enum "iso88022llc" { + value 41; + } + enum "localTalk" { + value 42; + } + enum "smdsDxi" { + value 43; + } + enum "frameRelayService" { + value 44; + description + "FRNETSERV-MIB"; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + enum "v35" { + value 45; + } + enum "hssi" { + value 46; + } + enum "hippi" { + value 47; + } + enum "modem" { + value 48; + description + "Generic modem"; + } + enum "aal5" { + value 49; + description + "AAL5 over ATM"; + } + enum "sonetPath" { + value 50; + } + enum "sonetVT" { + value 51; + } + enum "smdsIcip" { + value 52; + description + "SMDS InterCarrier Interface"; + } + enum "propVirtual" { + value 53; + description + "proprietary virtual/internal"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "propMultiplexor" { + value 54; + description + "proprietary multiplexing"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "ieee80212" { + value 55; + description + "100BaseVG"; + } + enum "fibreChannel" { + value 56; + description + "Fibre Channel"; + } + enum "hippiInterface" { + value 57; + description + "HIPPI interfaces"; + } + enum "frameRelayInterconnect" { + value 58; + status obsolete; + description + "Obsolete use either + frameRelay(32) or frameRelayService(44)."; + } + enum "aflane8023" { + value 59; + description + "ATM Emulated LAN for 802.3"; + } + enum "aflane8025" { + value 60; + description + "ATM Emulated LAN for 802.5"; + } + enum "cctEmul" { + value 61; + description + "ATM Emulated circuit"; + } + enum "fastEther" { + value 62; + status deprecated; + description + "Obsoleted via RFC3635. + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "isdn" { + value 63; + description + "ISDN and X.25"; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + enum "v11" { + value 64; + description + "CCITT V.11/X.21"; + } + enum "v36" { + value 65; + description + "CCITT V.36"; + } + enum "g703at64k" { + value 66; + description + "CCITT G703 at 64Kbps"; + } + enum "g703at2mb" { + value 67; + status obsolete; + description + "Obsolete see DS1-MIB"; + } + enum "qllc" { + value 68; + description + "SNA QLLC"; + } + enum "fastEtherFX" { + value 69; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "channel" { + value 70; + description + "channel"; + } + enum "ieee80211" { + value 71; + description + "radio spread spectrum"; + } + enum "ibm370parChan" { + value 72; + description + "IBM System 360/370 OEMI Channel"; + } + enum "escon" { + value 73; + description + "IBM Enterprise Systems Connection"; + } + enum "dlsw" { + value 74; + description + "Data Link Switching"; + } + enum "isdns" { + value 75; + description + "ISDN S/T interface"; + } + enum "isdnu" { + value 76; + description + "ISDN U interface"; + } + enum "lapd" { + value 77; + description + "Link Access Protocol D"; + } + enum "ipSwitch" { + value 78; + description + "IP Switching Objects"; + } + enum "rsrb" { + value 79; + description + "Remote Source Route Bridging"; + } + enum "atmLogical" { + value 80; + description + "ATM Logical Port"; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + enum "ds0" { + value 81; + description + "Digital Signal Level 0"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "ds0Bundle" { + value 82; + description + "group of ds0s on the same ds1"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "bsc" { + value 83; + description + "Bisynchronous Protocol"; + } + enum "async" { + value 84; + description + "Asynchronous Protocol"; + } + enum "cnr" { + value 85; + description + "Combat Net Radio"; + } + enum "iso88025Dtr" { + value 86; + description + "ISO 802.5r DTR"; + } + enum "eplrs" { + value 87; + description + "Ext Pos Loc Report Sys"; + } + enum "arap" { + value 88; + description + "Appletalk Remote Access Protocol"; + } + enum "propCnls" { + value 89; + description + "Proprietary Connectionless Protocol"; + } + enum "hostPad" { + value 90; + description + "CCITT-ITU X.29 PAD Protocol"; + } + enum "termPad" { + value 91; + description + "CCITT-ITU X.3 PAD Facility"; + } + enum "frameRelayMPI" { + value 92; + description + "Multiproto Interconnect over FR"; + } + enum "x213" { + value 93; + description + "CCITT-ITU X213"; + } + enum "adsl" { + value 94; + description + "Asymmetric Digital Subscriber Loop"; + } + enum "radsl" { + value 95; + description + "Rate-Adapt. Digital Subscriber Loop"; + } + enum "sdsl" { + value 96; + description + "Symmetric Digital Subscriber Loop"; + } + enum "vdsl" { + value 97; + description + "Very H-Speed Digital Subscrib. Loop"; + } + enum "iso88025CRFPInt" { + value 98; + description + "ISO 802.5 CRFP"; + } + enum "myrinet" { + value 99; + description + "Myricom Myrinet"; + } + enum "voiceEM" { + value 100; + description + "voice recEive and transMit"; + } + enum "voiceFXO" { + value 101; + description + "voice Foreign Exchange Office"; + } + enum "voiceFXS" { + value 102; + description + "voice Foreign Exchange Station"; + } + enum "voiceEncap" { + value 103; + description + "voice encapsulation"; + } + enum "voiceOverIp" { + value 104; + description + "voice over IP encapsulation"; + } + enum "atmDxi" { + value 105; + description + "ATM DXI"; + } + enum "atmFuni" { + value 106; + description + "ATM FUNI"; + } + enum "atmIma" { + value 107; + description + "ATM IMA"; + } + enum "pppMultilinkBundle" { + value 108; + description + "PPP Multilink Bundle"; + } + enum "ipOverCdlc" { + value 109; + description + "IBM ipOverCdlc"; + } + enum "ipOverClaw" { + value 110; + description + "IBM Common Link Access to Workstn"; + } + enum "stackToStack" { + value 111; + description + "IBM stackToStack"; + } + enum "virtualIpAddress" { + value 112; + description + "IBM VIPA"; + } + enum "mpc" { + value 113; + description + "IBM multi-protocol channel support"; + } + enum "ipOverAtm" { + value 114; + description + "IBM ipOverAtm"; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + enum "iso88025Fiber" { + value 115; + description + "ISO 802.5j Fiber Token Ring"; + } + enum "tdlc" { + value 116; + description + "IBM twinaxial data link control"; + } + enum "gigabitEthernet" { + value 117; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "hdlc" { + value 118; + description + "HDLC"; + } + enum "lapf" { + value 119; + description + "LAP F"; + } + enum "v37" { + value 120; + description + "V.37"; + } + enum "x25mlp" { + value 121; + description + "Multi-Link Protocol"; + } + enum "x25huntGroup" { + value 122; + description + "X25 Hunt Group"; + } + enum "transpHdlc" { + value 123; + description + "Transp HDLC"; + } + enum "interleave" { + value 124; + description + "Interleave channel"; + } + enum "fast" { + value 125; + description + "Fast channel"; + } + enum "ip" { + value 126; + description + "IP (for APPN HPR in IP networks)"; + } + enum "docsCableMaclayer" { + value 127; + description + "CATV Mac Layer"; + } + enum "docsCableDownstream" { + value 128; + description + "CATV Downstream interface"; + } + enum "docsCableUpstream" { + value 129; + description + "CATV Upstream interface"; + } + enum "a12MppSwitch" { + value 130; + description + "Avalon Parallel Processor"; + } + enum "tunnel" { + value 131; + description + "Encapsulation interface"; + } + enum "coffee" { + value 132; + description + "coffee pot"; + reference + "RFC 2325 - Coffee MIB"; + } + enum "ces" { + value 133; + description + "Circuit Emulation Service"; + } + enum "atmSubInterface" { + value 134; + description + "ATM Sub Interface"; + } + enum "l2vlan" { + value 135; + description + "Layer 2 Virtual LAN using 802.1Q"; + } + enum "l3ipvlan" { + value 136; + description + "Layer 3 Virtual LAN using IP"; + } + enum "l3ipxvlan" { + value 137; + description + "Layer 3 Virtual LAN using IPX"; + } + enum "digitalPowerline" { + value 138; + description + "IP over Power Lines"; + } + enum "mediaMailOverIp" { + value 139; + description + "Multimedia Mail over IP"; + } + enum "dtm" { + value 140; + description + "Dynamic syncronous Transfer Mode"; + } + enum "dcn" { + value 141; + description + "Data Communications Network"; + } + enum "ipForward" { + value 142; + description + "IP Forwarding Interface"; + } + enum "msdsl" { + value 143; + description + "Multi-rate Symmetric DSL"; + } + enum "ieee1394" { + value 144; + description + "IEEE1394 High Performance Serial Bus"; + } + enum "if-gsn" { + value 145; + description + "HIPPI-6400"; + } + enum "dvbRccMacLayer" { + value 146; + description + "DVB-RCC MAC Layer"; + } + enum "dvbRccDownstream" { + value 147; + description + "DVB-RCC Downstream Channel"; + } + enum "dvbRccUpstream" { + value 148; + description + "DVB-RCC Upstream Channel"; + } + enum "atmVirtual" { + value 149; + description + "ATM Virtual Interface"; + } + enum "mplsTunnel" { + value 150; + description + "MPLS Tunnel Virtual Interface"; + } + enum "srp" { + value 151; + description + "Spatial Reuse Protocol"; + } + enum "voiceOverAtm" { + value 152; + description + "Voice Over ATM"; + } + enum "voiceOverFrameRelay" { + value 153; + description + "Voice Over Frame Relay"; + } + enum "idsl" { + value 154; + description + "Digital Subscriber Loop over ISDN"; + } + enum "compositeLink" { + value 155; + description + "Avici Composite Link Interface"; + } + enum "ss7SigLink" { + value 156; + description + "SS7 Signaling Link"; + } + enum "propWirelessP2P" { + value 157; + description + "Prop. P2P wireless interface"; + } + enum "frForward" { + value 158; + description + "Frame Forward Interface"; + } + enum "rfc1483" { + value 159; + description + "Multiprotocol over ATM AAL5"; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + enum "usb" { + value 160; + description + "USB Interface"; + } + enum "ieee8023adLag" { + value 161; + description + "IEEE 802.3ad Link Aggregate"; + } + enum "bgppolicyaccounting" { + value 162; + description + "BGP Policy Accounting"; + } + enum "frf16MfrBundle" { + value 163; + description + "FRF .16 Multilink Frame Relay"; + } + enum "h323Gatekeeper" { + value 164; + description + "H323 Gatekeeper"; + } + enum "h323Proxy" { + value 165; + description + "H323 Voice and Video Proxy"; + } + enum "mpls" { + value 166; + description + "MPLS"; + } + enum "mfSigLink" { + value 167; + description + "Multi-frequency signaling link"; + } + enum "hdsl2" { + value 168; + description + "High Bit-Rate DSL - 2nd generation"; + } + enum "shdsl" { + value 169; + description + "Multirate HDSL2"; + } + enum "ds1FDL" { + value 170; + description + "Facility Data Link 4Kbps on a DS1"; + } + enum "pos" { + value 171; + description + "Packet over SONET/SDH Interface"; + } + enum "dvbAsiIn" { + value 172; + description + "DVB-ASI Input"; + } + enum "dvbAsiOut" { + value 173; + description + "DVB-ASI Output"; + } + enum "plc" { + value 174; + description + "Power Line Communtications"; + } + enum "nfas" { + value 175; + description + "Non Facility Associated Signaling"; + } + enum "tr008" { + value 176; + description + "TR008"; + } + enum "gr303RDT" { + value 177; + description + "Remote Digital Terminal"; + } + enum "gr303IDT" { + value 178; + description + "Integrated Digital Terminal"; + } + enum "isup" { + value 179; + description + "ISUP"; + } + enum "propDocsWirelessMaclayer" { + value 180; + description + "Cisco proprietary Maclayer"; + } + enum "propDocsWirelessDownstream" { + value 181; + description + "Cisco proprietary Downstream"; + } + enum "propDocsWirelessUpstream" { + value 182; + description + "Cisco proprietary Upstream"; + } + enum "hiperlan2" { + value 183; + description + "HIPERLAN Type 2 Radio Interface"; + } + enum "propBWAp2Mp" { + value 184; + description + "PropBroadbandWirelessAccesspt2multipt use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated and ieee80216WMAN(237) should be used + instead."; + } + enum "sonetOverheadChannel" { + value 185; + description + "SONET Overhead Channel"; + } + enum "digitalWrapperOverheadChannel" { + value 186; + description + "Digital Wrapper"; + } + enum "aal2" { + value 187; + description + "ATM adaptation layer 2"; + } + enum "radioMAC" { + value 188; + description + "MAC layer over radio links"; + } + enum "atmRadio" { + value 189; + description + "ATM over radio links"; + } + enum "imt" { + value 190; + description + "Inter Machine Trunks"; + } + enum "mvl" { + value 191; + description + "Multiple Virtual Lines DSL"; + } + enum "reachDSL" { + value 192; + description + "Long Reach DSL"; + } + enum "frDlciEndPt" { + value 193; + description + "Frame Relay DLCI End Point"; + } + enum "atmVciEndPt" { + value 194; + description + "ATM VCI End Point"; + } + enum "opticalChannel" { + value 195; + description + "Optical Channel"; + } + enum "opticalTransport" { + value 196; + description + "Optical Transport"; + } + enum "propAtm" { + value 197; + description + "Proprietary ATM"; + } + enum "voiceOverCable" { + value 198; + description + "Voice Over Cable Interface"; + } + enum "infiniband" { + value 199; + description + "Infiniband"; + } + enum "teLink" { + value 200; + description + "TE Link"; + } + enum "q2931" { + value 201; + description + "Q.2931"; + } + enum "virtualTg" { + value 202; + description + "Virtual Trunk Group"; + } + enum "sipTg" { + value 203; + description + "SIP Trunk Group"; + } + enum "sipSig" { + value 204; + description + "SIP Signaling"; + } + enum "docsCableUpstreamChannel" { + value 205; + description + "CATV Upstream Channel"; + } + enum "econet" { + value 206; + description + "Acorn Econet"; + } + enum "pon155" { + value 207; + description + "FSAN 155Mb Symetrical PON interface"; + } + enum "pon622" { + value 208; + description + "FSAN622Mb Symetrical PON interface"; + } + enum "bridge" { + value 209; + description + "Transparent bridge interface"; + } + enum "linegroup" { + value 210; + description + "Interface common to multiple lines"; + } + enum "voiceEMFGD" { + value 211; + description + "voice E&M Feature Group D"; + } + enum "voiceFGDEANA" { + value 212; + description + "voice FGD Exchange Access North American"; + } + enum "voiceDID" { + value 213; + description + "voice Direct Inward Dialing"; + } + enum "mpegTransport" { + value 214; + description + "MPEG transport interface"; + } + enum "sixToFour" { + value 215; + status deprecated; + description + "6to4 interface (DEPRECATED)"; + reference + "RFC 4087 - IP Tunnel MIB"; + } + enum "gtp" { + value 216; + description + "GTP (GPRS Tunneling Protocol)"; + } + enum "pdnEtherLoop1" { + value 217; + description + "Paradyne EtherLoop 1"; + } + enum "pdnEtherLoop2" { + value 218; + description + "Paradyne EtherLoop 2"; + } + enum "opticalChannelGroup" { + value 219; + description + "Optical Channel Group"; + } + enum "homepna" { + value 220; + description + "HomePNA ITU-T G.989"; + } + enum "gfp" { + value 221; + description + "Generic Framing Procedure (GFP)"; + } + enum "ciscoISLvlan" { + value 222; + description + "Layer 2 Virtual LAN using Cisco ISL"; + } + enum "actelisMetaLOOP" { + value 223; + description + "Acteleis proprietary MetaLOOP High Speed Link"; + } + enum "fcipLink" { + value 224; + description + "FCIP Link"; + } + enum "rpr" { + value 225; + description + "Resilient Packet Ring Interface Type"; + } + enum "qam" { + value 226; + description + "RF Qam Interface"; + } + enum "lmp" { + value 227; + description + "Link Management Protocol"; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + enum "cblVectaStar" { + value 228; + description + "Cambridge Broadband Networks Limited VectaStar"; + } + enum "docsCableMCmtsDownstream" { + value 229; + description + "CATV Modular CMTS Downstream Interface"; + } + enum "adsl2" { + value 230; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)"; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + enum "macSecControlledIF" { + value 231; + description + "MACSecControlled"; + } + enum "macSecUncontrolledIF" { + value 232; + description + "MACSecUncontrolled"; + } + enum "aviciOpticalEther" { + value 233; + description + "Avici Optical Ethernet Aggregate"; + } + enum "atmbond" { + value 234; + description + "atmbond"; + } + enum "voiceFGDOS" { + value 235; + description + "voice FGD Operator Services"; + } + enum "mocaVersion1" { + value 236; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA"; + } + enum "ieee80216WMAN" { + value 237; + description + "IEEE 802.16 WMAN interface"; + } + enum "adsl2plus" { + value 238; + description + "Asymmetric Digital Subscriber Loop Version 2, + Version 2 Plus and all variants"; + } + enum "dvbRcsMacLayer" { + value 239; + description + "DVB-RCS MAC Layer"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbTdm" { + value 240; + description + "DVB Satellite TDM"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbRcsTdma" { + value 241; + description + "DVB-RCS TDMA"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "x86Laps" { + value 242; + description + "LAPS based on ITU-T X.86/Y.1323"; + } + enum "wwanPP" { + value 243; + description + "3GPP WWAN"; + } + enum "wwanPP2" { + value 244; + description + "3GPP2 WWAN"; + } + enum "voiceEBS" { + value 245; + description + "voice P-phone EBS physical interface"; + } + enum "ifPwType" { + value 246; + description + "Pseudowire interface type"; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base"; + } + enum "ilan" { + value 247; + description + "Internal LAN on a bridge per IEEE 802.1ap"; + } + enum "pip" { + value 248; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB"; + } + enum "aluELP" { + value 249; + description + "Alcatel-Lucent Ethernet Link Protection"; + } + enum "gpon" { + value 250; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948"; + } + enum "vdsl2" { + value 251; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)"; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + enum "capwapDot11Profile" { + value 252; + description + "WLAN Profile Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapDot11Bss" { + value 253; + description + "WLAN BSS Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapWtpVirtualRadio" { + value 254; + description + "WTP Virtual Radio Interface"; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + enum "bits" { + value 255; + description + "bitsport"; + } + enum "docsCableUpstreamRfPort" { + value 256; + description + "DOCSIS CATV Upstream RF Port"; + } + enum "cableDownstreamRfPort" { + value 257; + description + "CATV downstream RF port"; + } + enum "vmwareVirtualNic" { + value 258; + description + "VMware Virtual Network Interface"; + } + enum "ieee802154" { + value 259; + description + "IEEE 802.15.4 WPAN interface"; + reference + "IEEE 802.15.4-2006"; + } + enum "otnOdu" { + value 260; + description + "OTN Optical Data Unit"; + } + enum "otnOtu" { + value 261; + description + "OTN Optical channel Transport Unit"; + } + enum "ifVfiType" { + value 262; + description + "VPLS Forwarding Instance Interface Type"; + } + enum "g9981" { + value 263; + description + "G.998.1 bonded interface"; + } + enum "g9982" { + value 264; + description + "G.998.2 bonded interface"; + } + enum "g9983" { + value 265; + description + "G.998.3 bonded interface"; + } + enum "aluEpon" { + value 266; + description + "Ethernet Passive Optical Networks (E-PON)"; + } + enum "aluEponOnu" { + value 267; + description + "EPON Optical Network Unit"; + } + enum "aluEponPhysicalUni" { + value 268; + description + "EPON physical User to Network interface"; + } + enum "aluEponLogicalLink" { + value 269; + description + "The emulation of a point-to-point link over the EPON + layer"; + } + enum "aluGponOnu" { + value 270; + description + "GPON Optical Network Unit"; + reference + "ITU-T G.984.2"; + } + enum "aluGponPhysicalUni" { + value 271; + description + "GPON physical User to Network interface"; + reference + "ITU-T G.984.2"; + } + enum "vmwareNicTeam" { + value 272; + description + "VMware NIC Team"; + } + // value 273 reserved by IANA + } + description + "This data type is used as the syntax of the 'type' + leaf in the 'interface' list in the YANG module + ietf-interface. + + The definition of this typedef with the + addition of newly assigned values is published + periodically by the IANA, in either the Assigned + Numbers RFC, or some derivative of it specific to + Internet Network Management number assignments. (The + latest arrangements can be obtained by contacting the + IANA.) + + Requests for new values should be made to IANA via + email (iana&iana.org)."; + reference + "IANA ifType definitions registry. + <http://www.iana.org/assignments/smi-numbers>"; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-interfaces@2013-07-04.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-interfaces@2013-07-04.yang new file mode 100644 index 0000000..9db753c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-interfaces@2013-07-04.yang @@ -0,0 +1,673 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + import iana-if-type { + prefix ianaift; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for Interface Management"; + } + + /* Typedefs */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* Features */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* Data nodes */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request, if the implementation does not support + pre-provisioning of interfaces, or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the feature 'pre-provisioning' is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the feature 'arbitrary-names' is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list. Since the name in that + list MAY be mapped to ifName by an implementation, such an + implementation MUST restrict the allowed values for this + leaf so that it matches the restrictions of ifName. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + This leaf MAY be mapped to ifAlias by an implementation. + Such an implementation MUST restrict the allowed values + for this leaf so that it matches the restrictions of + ifAlias. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation if + it modifies this single leaf in 'startup', or if it + performs an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces which do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + This leaf MAY be mapped to ifName by an implementation."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a MAC address. The interface's media-specific + modules must define the bit and byte ordering and the + format of the value of this object. For interfaces that do + not have such an address (e.g., a serial line), this node + is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits / second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface which were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing the number of transmission + units received via the interface which were discarded + because of an unknown or unsupported protocol. For any + interface that does not support protocol multiplexing, + this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf@2013-10-19.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf@2013-10-19.yang new file mode 100644 index 0000000..16766b0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-restconf@2013-10-19.yang @@ -0,0 +1,684 @@ +module ietf-restconf { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module1.yang new file mode 100644 index 0000000..604fe94 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module1.yang @@ -0,0 +1,12 @@ +module module1 { + namespace "module:1"; + prefix "mod1"; + revision "2014-01-01"; + + rpc dummy-rpc1-module1 { + } + + rpc dummy-rpc2-module1 { + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module2.yang new file mode 100644 index 0000000..7b359f3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module2.yang @@ -0,0 +1,11 @@ +module module2 { + namespace "module:2"; + prefix "mod2"; + revision "2014-01-02"; + + rpc dummy-rpc1-module2 { + } + + rpc dummy-rpc2-module2 { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module3.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module3.yang new file mode 100644 index 0000000..39bb690 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/module3.yang @@ -0,0 +1,5 @@ +module module3 { + namespace "module:3"; + prefix "mod3"; + revision "2014-01-03"; +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang new file mode 100644 index 0000000..7bd0003 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/iana-if-type.yang @@ -0,0 +1,1517 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org"; + description + "This YANG module defines the iana-if-type typedef, which + contains YANG definitions for IANA-registered interface types. + + This YANG module is maintained by IANA, and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: IANA Interface Type YANG Module"; + } + + typedef iana-if-type { + type enumeration { + enum "other" { + value 1; + description + "None of the following"; + } + enum "regular1822" { + value 2; + } + enum "hdh1822" { + value 3; + } + enum "ddnX25" { + value 4; + } + enum "rfc877x25" { + value 5; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + enum "ethernetCsmacd" { + value 6; + description + "For all ethernet-like interfaces, regardless of speed, + as per RFC3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88023Csmacd" { + value 7; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "iso88024TokenBus" { + value 8; + } + enum "iso88025TokenRing" { + value 9; + } + enum "iso88026Man" { + value 10; + } + enum "starLan" { + value 11; + status deprecated; + description + "Deprecated via RFC3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "proteon10Mbit" { + value 12; + } + enum "proteon80Mbit" { + value 13; + } + enum "hyperchannel" { + value 14; + } + enum "fddi" { + value 15; + reference + "RFC 1512 - FDDI Management Information Base"; + } + enum "lapb" { + value 16; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + enum "sdlc" { + value 17; + } + enum "ds1" { + value 18; + description + "DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "e1" { + value 19; + status obsolete; + description + "Obsolete see DS1-MIB"; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + enum "basicISDN" { + value 20; + description + "see also RFC2127"; + } + enum "primaryISDN" { + value 21; + } + enum "propPointToPointSerial" { + value 22; + description + "proprietary serial"; + } + enum "ppp" { + value 23; + } + enum "softwareLoopback" { + value 24; + } + enum "eon" { + value 25; + description + "CLNP over IP"; + } + enum "ethernet3Mbit" { + value 26; + } + enum "nsip" { + value 27; + description + "XNS over IP"; + } + enum "slip" { + value 28; + description + "generic SLIP"; + } + enum "ultra" { + value 29; + description + "ULTRA technologies"; + } + enum "ds3" { + value 30; + description + "DS3-MIB"; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + enum "sip" { + value 31; + description + "SMDS, coffee"; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + enum "frameRelay" { + value 32; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + enum "rs232" { + value 33; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + enum "para" { + value 34; + description + "parallel-port"; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + enum "arcnet" { + value 35; + description + "arcnet"; + } + enum "arcnetPlus" { + value 36; + description + "arcnet plus"; + } + enum "atm" { + value 37; + description + "ATM cells"; + } + enum "miox25" { + value 38; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + enum "sonet" { + value 39; + description + "SONET or SDH"; + } + enum "x25ple" { + value 40; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + enum "iso88022llc" { + value 41; + } + enum "localTalk" { + value 42; + } + enum "smdsDxi" { + value 43; + } + enum "frameRelayService" { + value 44; + description + "FRNETSERV-MIB"; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + enum "v35" { + value 45; + } + enum "hssi" { + value 46; + } + enum "hippi" { + value 47; + } + enum "modem" { + value 48; + description + "Generic modem"; + } + enum "aal5" { + value 49; + description + "AAL5 over ATM"; + } + enum "sonetPath" { + value 50; + } + enum "sonetVT" { + value 51; + } + enum "smdsIcip" { + value 52; + description + "SMDS InterCarrier Interface"; + } + enum "propVirtual" { + value 53; + description + "proprietary virtual/internal"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "propMultiplexor" { + value 54; + description + "proprietary multiplexing"; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + enum "ieee80212" { + value 55; + description + "100BaseVG"; + } + enum "fibreChannel" { + value 56; + description + "Fibre Channel"; + } + enum "hippiInterface" { + value 57; + description + "HIPPI interfaces"; + } + enum "frameRelayInterconnect" { + value 58; + status obsolete; + description + "Obsolete use either + frameRelay(32) or frameRelayService(44)."; + } + enum "aflane8023" { + value 59; + description + "ATM Emulated LAN for 802.3"; + } + enum "aflane8025" { + value 60; + description + "ATM Emulated LAN for 802.5"; + } + enum "cctEmul" { + value 61; + description + "ATM Emulated circuit"; + } + enum "fastEther" { + value 62; + status deprecated; + description + "Obsoleted via RFC3635. + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "isdn" { + value 63; + description + "ISDN and X.25"; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + enum "v11" { + value 64; + description + "CCITT V.11/X.21"; + } + enum "v36" { + value 65; + description + "CCITT V.36"; + } + enum "g703at64k" { + value 66; + description + "CCITT G703 at 64Kbps"; + } + enum "g703at2mb" { + value 67; + status obsolete; + description + "Obsolete see DS1-MIB"; + } + enum "qllc" { + value 68; + description + "SNA QLLC"; + } + enum "fastEtherFX" { + value 69; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "channel" { + value 70; + description + "channel"; + } + enum "ieee80211" { + value 71; + description + "radio spread spectrum"; + } + enum "ibm370parChan" { + value 72; + description + "IBM System 360/370 OEMI Channel"; + } + enum "escon" { + value 73; + description + "IBM Enterprise Systems Connection"; + } + enum "dlsw" { + value 74; + description + "Data Link Switching"; + } + enum "isdns" { + value 75; + description + "ISDN S/T interface"; + } + enum "isdnu" { + value 76; + description + "ISDN U interface"; + } + enum "lapd" { + value 77; + description + "Link Access Protocol D"; + } + enum "ipSwitch" { + value 78; + description + "IP Switching Objects"; + } + enum "rsrb" { + value 79; + description + "Remote Source Route Bridging"; + } + enum "atmLogical" { + value 80; + description + "ATM Logical Port"; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + enum "ds0" { + value 81; + description + "Digital Signal Level 0"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "ds0Bundle" { + value 82; + description + "group of ds0s on the same ds1"; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + enum "bsc" { + value 83; + description + "Bisynchronous Protocol"; + } + enum "async" { + value 84; + description + "Asynchronous Protocol"; + } + enum "cnr" { + value 85; + description + "Combat Net Radio"; + } + enum "iso88025Dtr" { + value 86; + description + "ISO 802.5r DTR"; + } + enum "eplrs" { + value 87; + description + "Ext Pos Loc Report Sys"; + } + enum "arap" { + value 88; + description + "Appletalk Remote Access Protocol"; + } + enum "propCnls" { + value 89; + description + "Proprietary Connectionless Protocol"; + } + enum "hostPad" { + value 90; + description + "CCITT-ITU X.29 PAD Protocol"; + } + enum "termPad" { + value 91; + description + "CCITT-ITU X.3 PAD Facility"; + } + enum "frameRelayMPI" { + value 92; + description + "Multiproto Interconnect over FR"; + } + enum "x213" { + value 93; + description + "CCITT-ITU X213"; + } + enum "adsl" { + value 94; + description + "Asymmetric Digital Subscriber Loop"; + } + enum "radsl" { + value 95; + description + "Rate-Adapt. Digital Subscriber Loop"; + } + enum "sdsl" { + value 96; + description + "Symmetric Digital Subscriber Loop"; + } + enum "vdsl" { + value 97; + description + "Very H-Speed Digital Subscrib. Loop"; + } + enum "iso88025CRFPInt" { + value 98; + description + "ISO 802.5 CRFP"; + } + enum "myrinet" { + value 99; + description + "Myricom Myrinet"; + } + enum "voiceEM" { + value 100; + description + "voice recEive and transMit"; + } + enum "voiceFXO" { + value 101; + description + "voice Foreign Exchange Office"; + } + enum "voiceFXS" { + value 102; + description + "voice Foreign Exchange Station"; + } + enum "voiceEncap" { + value 103; + description + "voice encapsulation"; + } + enum "voiceOverIp" { + value 104; + description + "voice over IP encapsulation"; + } + enum "atmDxi" { + value 105; + description + "ATM DXI"; + } + enum "atmFuni" { + value 106; + description + "ATM FUNI"; + } + enum "atmIma" { + value 107; + description + "ATM IMA"; + } + enum "pppMultilinkBundle" { + value 108; + description + "PPP Multilink Bundle"; + } + enum "ipOverCdlc" { + value 109; + description + "IBM ipOverCdlc"; + } + enum "ipOverClaw" { + value 110; + description + "IBM Common Link Access to Workstn"; + } + enum "stackToStack" { + value 111; + description + "IBM stackToStack"; + } + enum "virtualIpAddress" { + value 112; + description + "IBM VIPA"; + } + enum "mpc" { + value 113; + description + "IBM multi-protocol channel support"; + } + enum "ipOverAtm" { + value 114; + description + "IBM ipOverAtm"; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + enum "iso88025Fiber" { + value 115; + description + "ISO 802.5j Fiber Token Ring"; + } + enum "tdlc" { + value 116; + description + "IBM twinaxial data link control"; + } + enum "gigabitEthernet" { + value 117; + status deprecated; + description + "Obsoleted via RFC3635 + ethernetCsmacd(6) should be used instead"; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types."; + } + enum "hdlc" { + value 118; + description + "HDLC"; + } + enum "lapf" { + value 119; + description + "LAP F"; + } + enum "v37" { + value 120; + description + "V.37"; + } + enum "x25mlp" { + value 121; + description + "Multi-Link Protocol"; + } + enum "x25huntGroup" { + value 122; + description + "X25 Hunt Group"; + } + enum "transpHdlc" { + value 123; + description + "Transp HDLC"; + } + enum "interleave" { + value 124; + description + "Interleave channel"; + } + enum "fast" { + value 125; + description + "Fast channel"; + } + enum "ip" { + value 126; + description + "IP (for APPN HPR in IP networks)"; + } + enum "docsCableMaclayer" { + value 127; + description + "CATV Mac Layer"; + } + enum "docsCableDownstream" { + value 128; + description + "CATV Downstream interface"; + } + enum "docsCableUpstream" { + value 129; + description + "CATV Upstream interface"; + } + enum "a12MppSwitch" { + value 130; + description + "Avalon Parallel Processor"; + } + enum "tunnel" { + value 131; + description + "Encapsulation interface"; + } + enum "coffee" { + value 132; + description + "coffee pot"; + reference + "RFC 2325 - Coffee MIB"; + } + enum "ces" { + value 133; + description + "Circuit Emulation Service"; + } + enum "atmSubInterface" { + value 134; + description + "ATM Sub Interface"; + } + enum "l2vlan" { + value 135; + description + "Layer 2 Virtual LAN using 802.1Q"; + } + enum "l3ipvlan" { + value 136; + description + "Layer 3 Virtual LAN using IP"; + } + enum "l3ipxvlan" { + value 137; + description + "Layer 3 Virtual LAN using IPX"; + } + enum "digitalPowerline" { + value 138; + description + "IP over Power Lines"; + } + enum "mediaMailOverIp" { + value 139; + description + "Multimedia Mail over IP"; + } + enum "dtm" { + value 140; + description + "Dynamic syncronous Transfer Mode"; + } + enum "dcn" { + value 141; + description + "Data Communications Network"; + } + enum "ipForward" { + value 142; + description + "IP Forwarding Interface"; + } + enum "msdsl" { + value 143; + description + "Multi-rate Symmetric DSL"; + } + enum "ieee1394" { + value 144; + description + "IEEE1394 High Performance Serial Bus"; + } + enum "if-gsn" { + value 145; + description + "HIPPI-6400"; + } + enum "dvbRccMacLayer" { + value 146; + description + "DVB-RCC MAC Layer"; + } + enum "dvbRccDownstream" { + value 147; + description + "DVB-RCC Downstream Channel"; + } + enum "dvbRccUpstream" { + value 148; + description + "DVB-RCC Upstream Channel"; + } + enum "atmVirtual" { + value 149; + description + "ATM Virtual Interface"; + } + enum "mplsTunnel" { + value 150; + description + "MPLS Tunnel Virtual Interface"; + } + enum "srp" { + value 151; + description + "Spatial Reuse Protocol"; + } + enum "voiceOverAtm" { + value 152; + description + "Voice Over ATM"; + } + enum "voiceOverFrameRelay" { + value 153; + description + "Voice Over Frame Relay"; + } + enum "idsl" { + value 154; + description + "Digital Subscriber Loop over ISDN"; + } + enum "compositeLink" { + value 155; + description + "Avici Composite Link Interface"; + } + enum "ss7SigLink" { + value 156; + description + "SS7 Signaling Link"; + } + enum "propWirelessP2P" { + value 157; + description + "Prop. P2P wireless interface"; + } + enum "frForward" { + value 158; + description + "Frame Forward Interface"; + } + enum "rfc1483" { + value 159; + description + "Multiprotocol over ATM AAL5"; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + enum "usb" { + value 160; + description + "USB Interface"; + } + enum "ieee8023adLag" { + value 161; + description + "IEEE 802.3ad Link Aggregate"; + } + enum "bgppolicyaccounting" { + value 162; + description + "BGP Policy Accounting"; + } + enum "frf16MfrBundle" { + value 163; + description + "FRF .16 Multilink Frame Relay"; + } + enum "h323Gatekeeper" { + value 164; + description + "H323 Gatekeeper"; + } + enum "h323Proxy" { + value 165; + description + "H323 Voice and Video Proxy"; + } + enum "mpls" { + value 166; + description + "MPLS"; + } + enum "mfSigLink" { + value 167; + description + "Multi-frequency signaling link"; + } + enum "hdsl2" { + value 168; + description + "High Bit-Rate DSL - 2nd generation"; + } + enum "shdsl" { + value 169; + description + "Multirate HDSL2"; + } + enum "ds1FDL" { + value 170; + description + "Facility Data Link 4Kbps on a DS1"; + } + enum "pos" { + value 171; + description + "Packet over SONET/SDH Interface"; + } + enum "dvbAsiIn" { + value 172; + description + "DVB-ASI Input"; + } + enum "dvbAsiOut" { + value 173; + description + "DVB-ASI Output"; + } + enum "plc" { + value 174; + description + "Power Line Communtications"; + } + enum "nfas" { + value 175; + description + "Non Facility Associated Signaling"; + } + enum "tr008" { + value 176; + description + "TR008"; + } + enum "gr303RDT" { + value 177; + description + "Remote Digital Terminal"; + } + enum "gr303IDT" { + value 178; + description + "Integrated Digital Terminal"; + } + enum "isup" { + value 179; + description + "ISUP"; + } + enum "propDocsWirelessMaclayer" { + value 180; + description + "Cisco proprietary Maclayer"; + } + enum "propDocsWirelessDownstream" { + value 181; + description + "Cisco proprietary Downstream"; + } + enum "propDocsWirelessUpstream" { + value 182; + description + "Cisco proprietary Upstream"; + } + enum "hiperlan2" { + value 183; + description + "HIPERLAN Type 2 Radio Interface"; + } + enum "propBWAp2Mp" { + value 184; + description + "PropBroadbandWirelessAccesspt2multipt use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated and ieee80216WMAN(237) should be used + instead."; + } + enum "sonetOverheadChannel" { + value 185; + description + "SONET Overhead Channel"; + } + enum "digitalWrapperOverheadChannel" { + value 186; + description + "Digital Wrapper"; + } + enum "aal2" { + value 187; + description + "ATM adaptation layer 2"; + } + enum "radioMAC" { + value 188; + description + "MAC layer over radio links"; + } + enum "atmRadio" { + value 189; + description + "ATM over radio links"; + } + enum "imt" { + value 190; + description + "Inter Machine Trunks"; + } + enum "mvl" { + value 191; + description + "Multiple Virtual Lines DSL"; + } + enum "reachDSL" { + value 192; + description + "Long Reach DSL"; + } + enum "frDlciEndPt" { + value 193; + description + "Frame Relay DLCI End Point"; + } + enum "atmVciEndPt" { + value 194; + description + "ATM VCI End Point"; + } + enum "opticalChannel" { + value 195; + description + "Optical Channel"; + } + enum "opticalTransport" { + value 196; + description + "Optical Transport"; + } + enum "propAtm" { + value 197; + description + "Proprietary ATM"; + } + enum "voiceOverCable" { + value 198; + description + "Voice Over Cable Interface"; + } + enum "infiniband" { + value 199; + description + "Infiniband"; + } + enum "teLink" { + value 200; + description + "TE Link"; + } + enum "q2931" { + value 201; + description + "Q.2931"; + } + enum "virtualTg" { + value 202; + description + "Virtual Trunk Group"; + } + enum "sipTg" { + value 203; + description + "SIP Trunk Group"; + } + enum "sipSig" { + value 204; + description + "SIP Signaling"; + } + enum "docsCableUpstreamChannel" { + value 205; + description + "CATV Upstream Channel"; + } + enum "econet" { + value 206; + description + "Acorn Econet"; + } + enum "pon155" { + value 207; + description + "FSAN 155Mb Symetrical PON interface"; + } + enum "pon622" { + value 208; + description + "FSAN622Mb Symetrical PON interface"; + } + enum "bridge" { + value 209; + description + "Transparent bridge interface"; + } + enum "linegroup" { + value 210; + description + "Interface common to multiple lines"; + } + enum "voiceEMFGD" { + value 211; + description + "voice E&M Feature Group D"; + } + enum "voiceFGDEANA" { + value 212; + description + "voice FGD Exchange Access North American"; + } + enum "voiceDID" { + value 213; + description + "voice Direct Inward Dialing"; + } + enum "mpegTransport" { + value 214; + description + "MPEG transport interface"; + } + enum "sixToFour" { + value 215; + status deprecated; + description + "6to4 interface (DEPRECATED)"; + reference + "RFC 4087 - IP Tunnel MIB"; + } + enum "gtp" { + value 216; + description + "GTP (GPRS Tunneling Protocol)"; + } + enum "pdnEtherLoop1" { + value 217; + description + "Paradyne EtherLoop 1"; + } + enum "pdnEtherLoop2" { + value 218; + description + "Paradyne EtherLoop 2"; + } + enum "opticalChannelGroup" { + value 219; + description + "Optical Channel Group"; + } + enum "homepna" { + value 220; + description + "HomePNA ITU-T G.989"; + } + enum "gfp" { + value 221; + description + "Generic Framing Procedure (GFP)"; + } + enum "ciscoISLvlan" { + value 222; + description + "Layer 2 Virtual LAN using Cisco ISL"; + } + enum "actelisMetaLOOP" { + value 223; + description + "Acteleis proprietary MetaLOOP High Speed Link"; + } + enum "fcipLink" { + value 224; + description + "FCIP Link"; + } + enum "rpr" { + value 225; + description + "Resilient Packet Ring Interface Type"; + } + enum "qam" { + value 226; + description + "RF Qam Interface"; + } + enum "lmp" { + value 227; + description + "Link Management Protocol"; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + enum "cblVectaStar" { + value 228; + description + "Cambridge Broadband Networks Limited VectaStar"; + } + enum "docsCableMCmtsDownstream" { + value 229; + description + "CATV Modular CMTS Downstream Interface"; + } + enum "adsl2" { + value 230; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)"; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + enum "macSecControlledIF" { + value 231; + description + "MACSecControlled"; + } + enum "macSecUncontrolledIF" { + value 232; + description + "MACSecUncontrolled"; + } + enum "aviciOpticalEther" { + value 233; + description + "Avici Optical Ethernet Aggregate"; + } + enum "atmbond" { + value 234; + description + "atmbond"; + } + enum "voiceFGDOS" { + value 235; + description + "voice FGD Operator Services"; + } + enum "mocaVersion1" { + value 236; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA"; + } + enum "ieee80216WMAN" { + value 237; + description + "IEEE 802.16 WMAN interface"; + } + enum "adsl2plus" { + value 238; + description + "Asymmetric Digital Subscriber Loop Version 2, + Version 2 Plus and all variants"; + } + enum "dvbRcsMacLayer" { + value 239; + description + "DVB-RCS MAC Layer"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbTdm" { + value 240; + description + "DVB Satellite TDM"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "dvbRcsTdma" { + value 241; + description + "DVB-RCS TDMA"; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + enum "x86Laps" { + value 242; + description + "LAPS based on ITU-T X.86/Y.1323"; + } + enum "wwanPP" { + value 243; + description + "3GPP WWAN"; + } + enum "wwanPP2" { + value 244; + description + "3GPP2 WWAN"; + } + enum "voiceEBS" { + value 245; + description + "voice P-phone EBS physical interface"; + } + enum "ifPwType" { + value 246; + description + "Pseudowire interface type"; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base"; + } + enum "ilan" { + value 247; + description + "Internal LAN on a bridge per IEEE 802.1ap"; + } + enum "pip" { + value 248; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB"; + } + enum "aluELP" { + value 249; + description + "Alcatel-Lucent Ethernet Link Protection"; + } + enum "gpon" { + value 250; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948"; + } + enum "vdsl2" { + value 251; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)"; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + enum "capwapDot11Profile" { + value 252; + description + "WLAN Profile Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapDot11Bss" { + value 253; + description + "WLAN BSS Interface"; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + enum "capwapWtpVirtualRadio" { + value 254; + description + "WTP Virtual Radio Interface"; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + enum "bits" { + value 255; + description + "bitsport"; + } + enum "docsCableUpstreamRfPort" { + value 256; + description + "DOCSIS CATV Upstream RF Port"; + } + enum "cableDownstreamRfPort" { + value 257; + description + "CATV downstream RF port"; + } + enum "vmwareVirtualNic" { + value 258; + description + "VMware Virtual Network Interface"; + } + enum "ieee802154" { + value 259; + description + "IEEE 802.15.4 WPAN interface"; + reference + "IEEE 802.15.4-2006"; + } + enum "otnOdu" { + value 260; + description + "OTN Optical Data Unit"; + } + enum "otnOtu" { + value 261; + description + "OTN Optical channel Transport Unit"; + } + enum "ifVfiType" { + value 262; + description + "VPLS Forwarding Instance Interface Type"; + } + enum "g9981" { + value 263; + description + "G.998.1 bonded interface"; + } + enum "g9982" { + value 264; + description + "G.998.2 bonded interface"; + } + enum "g9983" { + value 265; + description + "G.998.3 bonded interface"; + } + enum "aluEpon" { + value 266; + description + "Ethernet Passive Optical Networks (E-PON)"; + } + enum "aluEponOnu" { + value 267; + description + "EPON Optical Network Unit"; + } + enum "aluEponPhysicalUni" { + value 268; + description + "EPON physical User to Network interface"; + } + enum "aluEponLogicalLink" { + value 269; + description + "The emulation of a point-to-point link over the EPON + layer"; + } + enum "aluGponOnu" { + value 270; + description + "GPON Optical Network Unit"; + reference + "ITU-T G.984.2"; + } + enum "aluGponPhysicalUni" { + value 271; + description + "GPON physical User to Network interface"; + reference + "ITU-T G.984.2"; + } + enum "vmwareNicTeam" { + value 272; + description + "VMware NIC Team"; + } + // value 273 reserved by IANA + } + description + "This data type is used as the syntax of the 'type' + leaf in the 'interface' list in the YANG module + ietf-interface. + + The definition of this typedef with the + addition of newly assigned values is published + periodically by the IANA, in either the Assigned + Numbers RFC, or some derivative of it specific to + Internet Network Management number assignments. (The + latest arrangements can be obtained by contacting the + IANA.) + + Requests for new values should be made to IANA via + email (iana&iana.org)."; + reference + "IANA ifType definitions registry. + <http://www.iana.org/assignments/smi-numbers>"; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang new file mode 100644 index 0000000..9db753c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-interfaces@2013-07-04.yang @@ -0,0 +1,673 @@ +module ietf-interfaces { + + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + import iana-if-type { + prefix ianaift; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + WG Chair: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com>"; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-07-04 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for Interface Management"; + } + + /* Typedefs */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + configured interfaces."; + } + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* Features */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + + feature if-mib { + description + "This feature indicates that the device implements IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* Data nodes */ + + container interfaces { + description + "Interface configuration parameters."; + + list interface { + key "name"; + + description + "The list of configured interfaces on the device. + + The operational state of an interface is available in the + /interfaces-state/interface list. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the + /interfaces-state/interface list. If the the configuration + of a user-controlled interface cannot be used by the system, + the configured interface is not instantiated in the + /interfaces-state/interface list."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list /interfaces-state/interface contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + /interfaces-state/interface list, the server MAY reject + the request, if the implementation does not support + pre-provisioning of interfaces, or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the feature 'pre-provisioning' is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the feature 'arbitrary-names' is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interface-state/interface list. Since the name in that + list MAY be mapped to ifName by an implementation, such an + implementation MUST restrict the allowed values for this + leaf so that it matches the restrictions of ifName. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'."; + } + + leaf description { + type string; + description + "A textual description of the interface. + + This leaf MAY be mapped to ifAlias by an implementation. + Such an implementation MUST restrict the allowed values + for this leaf so that it matches the restrictions of + ifAlias. + + If a NETCONF server that implements this restriction is + sent a value that doesn't match the restriction, it MUST + reply with an rpc-error with the error-tag + 'invalid-value'. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation if + it modifies this single leaf in 'startup', or if it + performs an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + } + enum disabled { + value 2; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces which do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + } + } + + container interfaces-state { + config false; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether they are configured or + not."; + + leaf name { + type string; + description + "The name of the interface. + + This leaf MAY be mapped to ifName by an implementation."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type ianaift:iana-if-type; + mandatory true; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a MAC address. The interface's media-specific + modules must define the bit and byte ordering and the + format of the value of this object. For interfaces that do + not have such an address (e.g., a serial line), this node + is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits / second"; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, which were addressed to a multicast + address at this sub-layer. For a MAC layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + leaf in-unknown-protos { + type yang:counter32; + description + "For packet-oriented interfaces, the number of packets + received via the interface which were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing the number of transmission + units received via the interface which were discarded + because of an unknown or unsupported protocol. For any + interface that does not support protocol multiplexing, + this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and which were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets which were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang new file mode 100644 index 0000000..16766b0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-restconf@2013-10-19.yang @@ -0,0 +1,684 @@ +module ietf-restconf { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang new file mode 100644 index 0000000..07e50b3 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module1-behind-mount-point.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module1-behind-mount-point.yang new file mode 100644 index 0000000..e48184b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module1-behind-mount-point.yang @@ -0,0 +1,10 @@ +module module1-behind-mount-point { + namespace "module:1:behind:mount:point"; + prefix "mod1bemopo"; + revision "2014-02-03"; + + rpc rpc-behind-module1 { + } + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module2-behind-mount-point.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module2-behind-mount-point.yang new file mode 100644 index 0000000..89b8c02 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-behind-mount-point/module2-behind-mount-point.yang @@ -0,0 +1,9 @@ +module module2-behind-mount-point { + namespace "module:2:behind:mount:point"; + prefix "mod2bemopo"; + revision "2014-02-04"; + + rpc rpc-behind-module2 { + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module1.yang new file mode 100644 index 0000000..ab9d967 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module1.yang @@ -0,0 +1,12 @@ +module module1 { + namespace "module:1"; + prefix "mod1"; + revision "2014-01-01"; + + rpc dummy-rpc1-module1 { + } + + rpc dummy-rpc2-module1 { + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module2.yang new file mode 100644 index 0000000..fa792d7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module2.yang @@ -0,0 +1,11 @@ +module module2 { + namespace "module:2"; + prefix "mod2"; + revision "2014-01-02"; + + rpc dummy-rpc1-module2 { + } + + rpc dummy-rpc2-module2 { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module3.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module3.yang new file mode 100644 index 0000000..39bb690 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/module3.yang @@ -0,0 +1,5 @@ +module module3 { + namespace "module:3"; + prefix "mod3"; + revision "2014-01-03"; +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/mount-point-1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/mount-point-1.yang new file mode 100644 index 0000000..4963c89 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/modules-without-restconf-module/mount-point-1.yang @@ -0,0 +1,11 @@ +module mount-point-1 { + namespace "mount:point:1"; + prefix "point1"; + revision "2016-01-01"; + + container cont { + } + + list listA { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-1.yang new file mode 100644 index 0000000..4963c89 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-1.yang @@ -0,0 +1,11 @@ +module mount-point-1 { + namespace "mount:point:1"; + prefix "point1"; + revision "2016-01-01"; + + container cont { + } + + list listA { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-2.yang new file mode 100644 index 0000000..c748cb7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/mount-points/mount-point-2.yang @@ -0,0 +1,8 @@ +module mount-point-2 { + namespace "mount:point:2"; + prefix "point2"; + revision "2016-01-01"; + + container cont { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/nested-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/nested-module.yang new file mode 100644 index 0000000..794859b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/nested-module.yang @@ -0,0 +1,69 @@ +module nested-module { + namespace "urn:nested:module"; + prefix "nested"; + revision "2014-06-03"; + + container depth1-cont { + list depth2-cont1 { + container depth3-cont1 { + container depth4-cont1 { + leaf depth5-leaf1 { + type string; + } + } + + leaf depth4-leaf1 { + type string; + } + } + + leaf depth3-leaf1 { + type string; + } + } + + /* list depth2-list2 was added to test keyed list */ + list depth2-list2 { + key "depth3-lf1-key depth3-lf2-key"; + leaf depth3-lf1-key { + type string; + } + leaf depth3-lf2-key { + type string; + } + leaf depth3-lf3 { + type string; + } + } + + leaf-list depth2-lfLst1 { + type string; + } + + container depth2-cont2 { + container depth3-cont2 { + container depth4-cont2 { + leaf depth5-leaf2 { + type string; + } + } + + leaf depth4-leaf2 { + type string; + } + } + + leaf depth3-leaf2 { + type string; + } + } + + leaf depth2-leaf1 { + type string; + } + } + + notification notifi{ + description "Notifi"; + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/mount-point-1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/mount-point-1.yang new file mode 100644 index 0000000..4963c89 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/mount-point-1.yang @@ -0,0 +1,11 @@ +module mount-point-1 { + namespace "mount:point:1"; + prefix "point1"; + revision "2016-01-01"; + + container cont { + } + + list listA { + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-container-modules/restconf-module-with-illegal-container-modules.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-container-modules/restconf-module-with-illegal-container-modules.yang new file mode 100644 index 0000000..116cdc1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-container-modules/restconf-module-with-illegal-container-modules.yang @@ -0,0 +1,685 @@ +module restconf-module-with-illegal-container-modules { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + /** changed from container modules to list modules for testing purposes **/ + list modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-list-module/restconf-module-with-illegal-list-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-list-module/restconf-module-with-illegal-list-module.yang new file mode 100644 index 0000000..2d8fbb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-illegal-list-module/restconf-module-with-illegal-list-module.yang @@ -0,0 +1,684 @@ +module restconf-module-with-illegal-list-module { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + /** changed from list module to container module for testing purposes **/ + container module { + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-container-modules/restconf-module-with-missing-container-modules.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-container-modules/restconf-module-with-missing-container-modules.yang new file mode 100644 index 0000000..907533d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-container-modules/restconf-module-with-missing-container-modules.yang @@ -0,0 +1,639 @@ +module restconf-module-with-missing-container-modules { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + // removed container modules for testing purposes + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-list-module/restconf-module-with-missing-list-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-list-module/restconf-module-with-missing-list-module.yang new file mode 100644 index 0000000..6d2b4da --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing-mount-point/restconf-module-with-missing-list-module/restconf-module-with-missing-list-module.yang @@ -0,0 +1,650 @@ +module restconf-module-with-missing-list-module { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + // removed list module for testing purposes + added list test-list + list test-list { + leaf test-leaf { + type string; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-restconf@2013-10-19.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-restconf@2013-10-19.yang new file mode 100644 index 0000000..bd8273a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-restconf@2013-10-19.yang @@ -0,0 +1,684 @@ +module ietf-restconf { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "restconf"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "Editor: Andy Bierman + <mailto:andy@yumaworks.com> + + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Editor: Kent Watsen + <mailto:kwatsen@juniper.net> + + Editor: Rex Fernando + <mailto:rex@cisco.com>"; + + description + "This module contains conceptual YANG specifications + for the YANG Patch and error content that is used in + RESTCONF protocol messages. A conceptual container + representing the RESTCONF API nodes (media type + application/yang.api). + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The YANG grouping statements provide a normative syntax + for XML and JSON message encoding purposes. + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: remove this note + // Note: extracted from draft-bierman-netconf-restconf-02.txt + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2013-10-19 { + description + "Initial revision."; + reference + "RFC XXXX: RESTCONF Protocol."; + } + + typedef data-resource-identifier { + type string { + length "1 .. max"; + } + description + "Contains a Data Resource Identifier formatted string + to identify a specific data node. The data node that + uses this data type SHOULD define the document root + for data resource identifiers. The default document + root is the target datastore conceptual root node. + Data resource identifiers are defined relative to + this document root."; + reference + "RFC XXXX: [sec. 5.3.1.1 ABNF For Data Resource Identifiers]"; + } + + // this typedef is TBD; not currently used + typedef datastore-identifier { + type union { + type enumeration { + enum candidate { + description + "Identifies the NETCONF shared candidate datastore."; + reference + "RFC 6241, section 8.3"; + } + enum running { + description + "Identifies the NETCONF running datastore."; + reference + "RFC 6241, section 5.1"; + } + enum startup { + description + "Identifies the NETCONF startup datastore."; + reference + "RFC 6241, section 8.7"; + } + } + type string; + } + description + "Contains a string to identify a specific datastore. + The enumerated datastore identifier values are + reserved for standard datastore names."; + } + + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format. + TBD: make pattern more precise to exclude leading zeros."; + } + + grouping yang-patch { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch edit request message."; + + container yang-patch { + description + "Represents a conceptual sequence of datastore edits, + called a patch. Each patch is given a client-assigned + patch identifier. Each edit MUST be applied + in ascending order, and all edits MUST be applied. + If any errors occur, then the target datastore MUST NOT + be changed by the patch operation. + + A patch MUST be validated by the server to be a + well-formed message before any of the patch edits + are validated or attempted. + + YANG datastore validation (defined in RFC 6020, section + 8.3.3) is performed after all edits have been + individually validated. + + It is possible for a datastore constraint violation to occur + due to any node in the datastore, including nodes not + included in the edit list. Any validation errors MUST + be reported in the reply message."; + + reference + "RFC 6020, section 8.3."; + + leaf patch-id { + type string; + description + "An arbitrary string provided by the client to identify + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch. Error messages returned by the server pertaining + to this patch will be identified by this patch-id value."; + } + + leaf comment { + type string { + length "0 .. 1024"; + } + description + "An arbitrary string provided by the client to describe + the entire patch. This value SHOULD be present in any + audit logging records generated by the server for the + patch."; + } + + list edit { + key edit-id; + ordered-by user; + + description + "Represents one edit within the YANG Patch + request message."; + leaf edit-id { + type string; + description + "Arbitrary string index for the edit. + Error messages returned by the server pertaining + to a specific edit will be identified by this + value."; + } + + leaf operation { + type enumeration { + enum create { + description + "The target data node is created using the + supplied value, only if it does not already + exist."; + } + enum delete { + description + "Delete the target node, only if the data resource + currently exists, otherwise return an error."; + } + enum insert { + description + "Insert the supplied value into a user-ordered + list or leaf-list entry. The target node must + represent a new data resource."; + } + enum merge { + description + "The supplied value is merged with the target data + node."; + } + enum move { + description + "Move the target node. Reorder a user-ordered + list or leaf-list. The target node must represent + an existing data resource."; + } + enum replace { + description + "The supplied value is used to replace the target + data node."; + } + enum remove { + description + "Delete the target node if it currently exists."; + } + } + mandatory true; + description + "The datastore operation requested for the associated + edit entry"; + } + + leaf target { + type data-resource-identifier; + mandatory true; + description + "Identifies the target data resource for the edit + operation."; + } + + leaf point { + when "(../operation = 'insert' or " + + "../operation = 'move') and " + + "(../where = 'before' or ../where = 'after')" { + description + "Point leaf only applies for insert or move + operations, before or after an existing entry."; + } + type data-resource-identifier; + description + "The absolute URL path for the data node that is being + used as the insertion point or move point for the + target of this edit entry."; + } + + leaf where { + when "../operation = 'insert' or ../operation = 'move'" { + description + "Where leaf only applies for insert or move + operations."; + } + type enumeration { + enum before { + description + "Insert or move a data node before the data resource + identified by the 'point' parameter."; + } + enum after { + description + "Insert or move a data node after the data resource + identified by the 'point' parameter."; + } + enum first { + description + "Insert or move a data node so it becomes ordered + as the first entry."; + } + enum last { + description + "Insert or move a data node so it becomes ordered + as the last entry."; + } + + } + default last; + description + "Identifies where a data resource will be inserted or + moved. YANG only allows these operations for + list and leaf-list data nodes that are ordered-by + user."; + } + + anyxml value { + when "(../operation = 'create' or " + + "../operation = 'merge' " + + "or ../operation = 'replace' or " + + "../operation = 'insert')" { + description + "Value node only used for create, merge, + replace, and insert operations"; + } + description + "Value used for this edit operation."; + } + } + } + + } // grouping yang-patch + + + grouping yang-patch-status { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + YANG Patch status response message."; + + container yang-patch-status { + description + "A container representing the response message + sent by the server after a YANG Patch edit + request message has been processed."; + + leaf patch-id { + type string; + description + "The patch-id value used in the request"; + } + + choice global-status { + description + "Report global errors or complete success. + If there is no case selected then errors + are reported in the edit-status container."; + + case global-errors { + uses errors; + description + "This container will be present if global + errors unrelated to a specific edit occurred."; + } + leaf ok { + type empty; + description + "This leaf will be present if the request succeeded + and there are no errors reported in the edit-status + container."; + } + } + + container edit-status { + description + "This container will be present if there are + edit-specific status responses to report."; + + list edit { + key edit-id; + + description + "Represents a list of status responses, + corresponding to edits in the YANG Patch + request message. If an edit entry was + skipped or not reached by the server, + then this list will not contain a corresponding + entry for that edit."; + + leaf edit-id { + type string; + description + "Response status is for the edit list entry + with this edit-id value."; + } + choice edit-status-choice { + description + "A choice between different types of status + responses for each edit entry."; + leaf ok { + type empty; + description + "This edit entry was invoked without any + errors detected by the server associated + with this edit."; + } + leaf location { + type inet:uri; + description + "Contains the Location header value that would be + returned if this edit causes a new resource to be + created. If the edit identified by the same edit-id + value was successfully invoked and a new resource + was created, then this field will be returned + instead of 'ok'."; + } + case errors { + uses errors; + description + "The server detected errors associated with the + edit identified by the same edit-id value."; + } + } + } + } + } + } // grouping yang-patch-status + + + grouping errors { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch errors report within a response message."; + + container errors { + config false; // needed so list error does not need a key + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference "RFC 6241, Section 4.3"; + + leaf error-type { + type enumeration { + enum transport { + description "The transport layer"; + } + enum rpc { + description "The rpc or notification layer"; + } + enum protocol { + description "The protocol operation layer"; + } + enum application { + description "The server application layer"; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error tag."; + } + + leaf error-path { + type data-resource-identifier; + description + "The target data resource identifier associated + with the error, if any."; + } + leaf error-message { + type string; + description + "A message describing the error."; + } + + container error-info { + description + "A container allowing additional information + to be included in the error report."; + // arbitrary anyxml content here + } + } + } + } // grouping errors + + + grouping restconf { + + description + "A grouping that contains a YANG container + representing the syntax and semantics of + the RESTCONF API resource."; + + container restconf { + description + "Conceptual container representing the + application/yang.api resource type."; + + container config { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + unified configuration datastore containing YANG data + nodes. The child nodes of this container are + configuration data resources (application/yang.data) + defined as top-level YANG data nodes from the modules + advertised by the server in /restconf/modules."; + } + + container operational { + description + "Container representing the application/yang.datastore + resource type. Represents the conceptual root of the + operational data supported by the server. The child + nodes of this container are operational data resources + (application/yang.data) defined as top-level + YANG data nodes from the modules advertised by + the server in /restconf/modules."; + } + + container modules { + description + "Contains a list of module description entries. + These modules are currently loaded into the server."; + + list module { + key "name revision"; + description + "Each entry represents one module currently + supported by the server."; + + leaf name { + type yang:yang-identifier; + description "The YANG module name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module revision date. An empty string is + used if no revision statement is present in the + YANG module."; + } + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server."; + } + leaf-list deviation { + type yang:yang-identifier; + description + "List of YANG deviation module names used by this + server to modify the conformance of the module + associated with this entry."; + } + } + } + + container operations { + description + "Container for all operation resources + (application/yang.operation), + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG rpc statement. + + E.g.; + + POST /restconf/operations/show-log-errors + + leaf show-log-errors { + type empty; + } + "; + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description "The stream name"; + reference "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description "Description of stream content"; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + description + "Indicates if replay buffer supported for this stream"; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + leaf events { + type empty; + description + "Represents the entry point for establishing + notification delivery via server sent events."; + } + } + } + + leaf version { + type enumeration { + enum "1.0" { + description + "Version 1.0 of the RESTCONF protocol."; + } + } + config false; + description + "Contains the RESTCONF protocol version."; + } + } + } // grouping restconf + + + grouping notification { + description + "Contains the notification message wrapper definition."; + + container notification { + description + "RESTCONF notification message wrapper."; + leaf event-time { + type yang:date-and-time; + mandatory true; + description + "The time the event was generated by the + event source."; + reference + "RFC 5277, section 4, <eventTime> element."; + } + + /* The YANG-specific notification container is encoded + * after the 'event-time' element. The format + * corresponds to the notificationContent element + * in RFC 5277, section 4. For example: + * + * module example-one { + * ... + * notification event1 { ... } + * + * } + * + * Encoded as element 'event1' in the namespace + * for module 'example-one'. + */ + } + } // grouping notification + + }
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/modules/restconf-module-testing/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/multiple-nodes/multiple-nodes.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/multiple-nodes/multiple-nodes.yang new file mode 100644 index 0000000..22a1dae --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/multiple-nodes/multiple-nodes.yang @@ -0,0 +1,17 @@ +module multiple-nodes { + namespace "multiple:nodes"; + prefix "mod1"; + revision "2014-06-23"; + + container cont { + container cont1 { + leaf lf11 { + type string; + } + } + + leaf lf1 { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-container.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-container.yang new file mode 100644 index 0000000..7efe4f7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-container.yang @@ -0,0 +1,22 @@ +module augment-container { + namespace "ns:augment:container"; + prefix "augcont"; + + + import yang {prefix yng; revision-date 2013-11-26;} + + + revision "2013-11-26" { + } + + augment "/yng:cont" { + container cont1 { + leaf lf11 { + type string; + } + } + } + + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaf.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaf.yang new file mode 100644 index 0000000..248d3bb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaf.yang @@ -0,0 +1,18 @@ +module augment-leaf { + namespace "ns:augment:leaf"; + prefix "auglf"; + + + import yang {prefix yng; revision-date 2013-11-26;} + + + revision "2013-11-26" { + } + + augment "/yng:cont" { + leaf lf2 { + type string; + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaflist.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaflist.yang new file mode 100644 index 0000000..1f4b937 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-leaflist.yang @@ -0,0 +1,20 @@ +module augment-leaflist { + namespace "ns:augment:leaflist"; + prefix "auglflst"; + + + import yang {prefix yng; revision-date 2013-11-26;} + + + revision "2013-11-26" { + } + + augment "/yng:cont" { + leaf-list lflst1 { + type string; + } + } + + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-list.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-list.yang new file mode 100644 index 0000000..a35a87e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/augment-list.yang @@ -0,0 +1,22 @@ +module augment-list { + namespace "ns:augment:list"; + prefix "auglst"; + + + import yang {prefix yng; revision-date 2013-11-26;} + + + revision "2013-11-26" { + } + + augment "/yng:cont" { + list lst1 { + leaf lf11 { + type string; + } + } + } + + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/xml/data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/xml/data.xml new file mode 100644 index 0000000..fec6209 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/xml/data.xml @@ -0,0 +1,16 @@ +<cont xmlns="ns:yang"> + <lf1>lf1</lf1> + <lf2>lf2</lf2> + <cont1> + <lf11>lf11</lf11> + </cont1> + <lst1> + <lf11>lf1_1</lf11> + </lst1> + <lst1> + <lf11>lf1_2</lf11> + </lst1> + <lflst1>lflst1_1</lflst1> + <lflst1>lflst1_2</lflst1> + <lflst1>lflst1_3</lflst1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/yang.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/yang.yang new file mode 100644 index 0000000..327280f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/augmentation/yang.yang @@ -0,0 +1,30 @@ +module yang { + namespace "ns:yang"; + + prefix "yng"; + revision 2013-11-26 { + } + + container cont { + leaf lf1 { + type string; + } + leaf lf2 { + type string; + } + container cont1{ + leaf lf11 { + type string; + } + } + + list lst1{ + leaf lf11 { + type string; + } + } + leaf-list lflst1{ + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/choice.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/choice.yang new file mode 100644 index 0000000..22430b9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/choice.yang @@ -0,0 +1,125 @@ +module choice-case-test { + namespace "choice:case:test"; + + prefix "chcatst"; + revision 2013-11-27 { + } + + container cont { + leaf lf1 { + type string; + } + + choice choi1 { + case a1 { + leaf lf1a { + type uint16; + } + choice choi1a { + case aa1 { + leaf lf1aa { + type string; + } + choice choi1aa { + case aaa1 { + leaf lf1aaa { + type string; + } + } + case aab1 { + leaf lf1aab { + type string; + } + } + } + } + case ab1 { + leaf lf1ab { + type string; + } + } + } + } + case b1 { + list lst1b { + leaf lf11b { + type string; + } + } + } + case c1 { + container cont1c { + leaf lf11c { + type string; + } + } + } + case d1 { + leaf-list lflst1d { + type string; + } + } + leaf e1 { + type uint32; + } + } + + choice choi2 { + case a2 { + leaf lf2a { + type string; + } + } + case b2 { + leaf lf2b { + type string; + } + } + } + + choice choi4 { + case a4 { + list lst4a { + choice choi4aa { + case aa1 { + leaf lf4aa { + type string; + } + } + case ab2 { + leaf lf4ab { + type int16; + } + } + } + } + } + case b4 { + leaf-list lflst4b { + type uint32; + } + } + + } + +/* equal identifiers in various cases are illegal 7.9.2 rfc6020 */ +/* + choice choi3 { + case 3a { + leaf lf3a { + type string; + } + } + case 3b { + leaf lf3b { + type string; + } + } + } +*/ + + } + + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_case_defined_without_case.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_case_defined_without_case.xml new file mode 100644 index 0000000..1d3f99f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_case_defined_without_case.xml @@ -0,0 +1,4 @@ +<cont xmlns="choice:case:test"> + <e1>45</e1> + <lf2b>lf2b val</lf2b> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_container.xml new file mode 100644 index 0000000..146e256 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_container.xml @@ -0,0 +1,5 @@ +<cont xmlns="choice:case:test"> + <cont1c> + <lf11c>lf11c val</lf11c> + </cont1c> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_leaflist.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_leaflist.xml new file mode 100644 index 0000000..f501e04 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_leaflist.xml @@ -0,0 +1,4 @@ +<cont xmlns="choice:case:test"> + <lflst1d>lflst1d_1 val</lflst1d> + <lflst1d>lflst1d_2 val</lflst1d> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_list.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_list.xml new file mode 100644 index 0000000..6694b48 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_list.xml @@ -0,0 +1,8 @@ +<cont xmlns="choice:case:test"> + <lst1b> + <lf11b>lf11b_1 val</lf11b> + </lst1b> + <lst1b> + <lf11b>lf11b_2 val</lf11b> + </lst1b> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level.xml new file mode 100644 index 0000000..3d2742a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level.xml @@ -0,0 +1,6 @@ +<cont xmlns="choice:case:test"> + <cont1c> + <lf11c>lf11c val</lf11c> + </cont1c> + <lf2b>lf2b value</lf2b> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level_various_paths_err.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level_various_paths_err.xml new file mode 100644 index 0000000..aa9e24c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_more_choices_same_level_various_paths_err.xml @@ -0,0 +1,7 @@ +<cont xmlns="choice:case:test"> + <cont1c> + <lf11c>lf11c val</lf11c> + </cont1c> + <lf2b>lf2b value</lf2b> + <lf2a>lf2b value</lf2a> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_no_first_case.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_no_first_case.xml new file mode 100644 index 0000000..cc6f019 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_no_first_case.xml @@ -0,0 +1,5 @@ +<cont xmlns="choice:case:test"> + <lf1>lf1 val</lf1> + <lf1a>121</lf1a> + <lf1ab>lf1ab val</lf1ab> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_random_level.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_random_level.xml new file mode 100644 index 0000000..05ca42f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_random_level.xml @@ -0,0 +1,6 @@ +<cont xmlns="choice:case:test"> + <lf1aa>lf1aa val</lf1aa> + <lf1>lf1 val</lf1> + <lf1a>121</lf1a> + <lf1aaa>lf1aaa val</lf1aaa> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_three_choices_same_level.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_three_choices_same_level.xml new file mode 100644 index 0000000..7e54301 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_three_choices_same_level.xml @@ -0,0 +1,13 @@ +<cont xmlns="choice:case:test"> + <lf1aaa>lf1aaa value</lf1aaa> + <lf2b>lf2b value</lf2b> + <lst4a> + <lf4ab>33</lf4ab> + </lst4a> + <lst4a> + <lf4ab>33</lf4ab> + </lst4a> + <lst4a> + <lf4ab>37</lf4ab> + </lst4a> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_various_path_err.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_various_path_err.xml new file mode 100644 index 0000000..5274679 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/choice/xml/data_various_path_err.xml @@ -0,0 +1,6 @@ +<cont xmlns="choice:case:test"> + <lf1aa>lf1aa val</lf1aa> + <lf1>lf1 val</lf1> + <lf1a>121</lf1a> + <lf1ab>lf1ab value</lf1ab> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module1.yang new file mode 100644 index 0000000..72a82ca --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module1.yang @@ -0,0 +1,20 @@ +module module1 { + namespace "module:one"; + + prefix "m1"; + revision 2014-01-17 { + } + + container cont_m1 { + leaf lf1_m1 { + type string; + } + uses confB_gr; + } + + grouping confB_gr { + container contB_m1 { + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module2.yang new file mode 100644 index 0000000..521d9c0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/data-of-several-modules/yang/module2.yang @@ -0,0 +1,20 @@ +module module2 { + namespace "module:two"; + + prefix "m2"; + revision 2014-01-17 { + } + + container cont_m2 { + leaf lf1_m2 { + type string; + } + uses confB_gr; + } + + grouping confB_gr { + container contB_m2 { + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identity-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identity-module.yang new file mode 100644 index 0000000..09a34c5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identity-module.yang @@ -0,0 +1,10 @@ +module identity-module { + namespace "identity:module"; + + prefix "idemod"; + revision 2013-12-02 { + } + + identity iden { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identityref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identityref-module.yang new file mode 100644 index 0000000..c5c8f62 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/identityref/identityref-module.yang @@ -0,0 +1,21 @@ +module identityref-module { + namespace "identityref:module"; + + prefix "iderefmod"; + + import identity-module {prefix idemo; revision-date 2013-12-02; } + + revision 2013-12-02 { + } + + container cont { + container cont1 { + leaf lf1 { + type identityref { + base "idemo:iden"; + } + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/invalid-top-level-element/invalid-top-level-element.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/invalid-top-level-element/invalid-top-level-element.yang new file mode 100644 index 0000000..a9df486 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/invalid-top-level-element/invalid-top-level-element.yang @@ -0,0 +1,13 @@ +module invalid-top-level-element { + namespace "invalid:top:level:element"; + + prefix "intoleel"; + revision 2013-12-17 { + } + + + leaf lf { + type string; + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/cont-augment-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/cont-augment-module.yang new file mode 100644 index 0000000..b54027e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/cont-augment-module.yang @@ -0,0 +1,39 @@ +module cont-augment-module { + namespace "cont:augment:module"; + + prefix "cntaugmod"; + + import main-module {prefix mamo; revision-date 2013-12-02;} + + revision 2013-12-02 { + + } + + augment "/mamo:cont" { + leaf-list lflst1 { + type leafref { + path "../mamo:lf1"; + } + } + + leaf lf4 { + type leafref { + path "../mamo:lf1"; + } + } + + /* reference to not leaf element */ + leaf lf6 { + type leafref { + path "../lflst1"; + } + } + + leaf lf7 { + type leafref { + path "../lf4"; + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/main-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/main-module.yang new file mode 100644 index 0000000..0d90d2b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/main-module.yang @@ -0,0 +1,50 @@ +module main-module { + namespace "main:module"; + + prefix "mainmod"; + revision 2013-12-02 { + } + + container cont { + leaf lf1 { + /* + *FIX ME + * If is this leaf lf1 called from cont-augment-module.yang + * from lf4, type that will be returned to the lf1 is string. + * Than there are failing tests because of we have string, + * do not number(uint32) + */ +// type uint32; + type string; + } + + container cont1 { + leaf lf11 { + /* + * FIX ME TOO WITH BAD PARSING + */ +// type boolean; + type string; + } + } + + leaf lf2 { + type leafref { + path "../lf1"; + } + } + + leaf lf3 { + type leafref { + path "/cont/cont1/lf11"; + } + } + + /* reference to nonexisting leaf */ + leaf lf5 { + type leafref { + path "/cont/lf"; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_absolut_ref_to_existing_leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_absolut_ref_to_existing_leaf.xml new file mode 100644 index 0000000..bd5b6a2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_absolut_ref_to_existing_leaf.xml @@ -0,0 +1,7 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <cont1> + <lf11>true</lf11> + </cont1> + <lf3>true</lf3> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_from_leafref_to_leafref.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_from_leafref_to_leafref.xml new file mode 100644 index 0000000..7b1c277 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_from_leafref_to_leafref.xml @@ -0,0 +1,4 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <cont-augment-module:lf7>200</cont-augment-module:lf7> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_non_existing_leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_non_existing_leaf.xml new file mode 100644 index 0000000..cdbd56b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_non_existing_leaf.xml @@ -0,0 +1,4 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <lf5>137</lf5> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_not_leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_not_leaf.xml new file mode 100644 index 0000000..953280b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_ref_to_not_leaf.xml @@ -0,0 +1,4 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <cont-augment-module:lf6>44</cont-augment-module:lf6> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_from_leaflist_to_existing_leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_from_leaflist_to_existing_leaf.xml new file mode 100644 index 0000000..8fe0ac4 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_from_leaflist_to_existing_leaf.xml @@ -0,0 +1,6 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <cont-augment-module:lflst1>345</cont-augment-module:lflst1> + <cont-augment-module:lflst1>346</cont-augment-module:lflst1> + <cont-augment-module:lflst1>347</cont-augment-module:lflst1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_to_existing_leaf.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_to_existing_leaf.xml new file mode 100644 index 0000000..14a2544 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/leafref/xml/data_relativ_ref_to_existing_leaf.xml @@ -0,0 +1,5 @@ +<cont xmlns="main:module" + xmlns:cont-augment-module="cont:augment:module"> + <lf1>121</lf1> + <lf2>121</lf2> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/list/list-types-module b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/list/list-types-module new file mode 100644 index 0000000..9bdea81 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/list/list-types-module @@ -0,0 +1,274 @@ +module simple-data-types { + namespace "simple:data:types"; + + prefix "smpdtp"; + revision 2013-11-12 { + } + + identity iden { + } + + typedef tpdfempty { + type empty; + } + + typedef tpdfbit { + type bits { + bit b1; + bit b2; + bit b3; + } + } + + typedef tpdfun4 { + type boolean; + } + + typedef tpdfun3 { + type union { + type tpdfbit; + type tpdfempty; + } + } + + typedef tpdfun2 { + type union { + type tpdfun3; + type tpdfun4; + } + } + + typedef tpdfun1 { + type union { + type uint8; + type decimal64 { + fraction-digits 2; + } + } + } + + container cont { + leaf lfnint8Min { + type int8; + } + leaf lfnint8Max { + type int8; + } + leaf lfnint16Min { + type int16; + } + leaf lfnint16Max { + type int16; + } + leaf lfnint32Min { + type int32; + } + leaf lfnint32Max { + type int32; + } + leaf lfnint64Min { + type int64; + } + leaf lfnint64Max { + type int64; + } + + leaf lfnuint8Max { + type uint8; + } + leaf lfnuint16Max { + type uint16; + } + leaf lfnuint32Max { + type uint32; + } + leaf lfuint64Max { + type uint64; + } + leaf lfstr { + type string; + } + leaf lfstr1 { + type string; + } + leaf lfbool1 { + type boolean; + } + leaf lfbool2 { + type boolean; + } + leaf lfbool3 { + type boolean; + } + leaf lfdecimal1 { + type decimal64 { + fraction-digits 2; + } + } + leaf lfdecimal2 { + type decimal64 { + fraction-digits 2; + } + } + leaf lfdecimal3 { + type decimal64 { + fraction-digits 2; + } + } + + leaf lfdecimal4 { + type decimal64 { + fraction-digits 2; + } + } + + + leaf lfdecimal6 { + type decimal64 { + fraction-digits 2; + } + } + + leaf lfenum { + type enumeration { + enum enum1; + enum enum2; + enum enum3; + enum enum4; + } + } + + leaf lfbits { + type bits { + bit bit1; + bit bit2; + bit bit3; + bit bit4; + } + } + + leaf lfbinary { + type binary; + } + + leaf lfref1 { //reference to string type + type leafref { + path "../lfstr"; + } + } + + leaf lfref2 { //reference to number type + type leafref { + path "../lfnint8Max"; + } + } + + leaf lfempty { + type empty; + } + + leaf lfunion1 { + type union { + type uint16; + type string; + } + } + leaf lfunion2 { + type union { + type decimal64 { + fraction-digits 2; + } + type string; + } + } + + leaf lfunion3 { + type union { + type empty; + type string; + } + } + + leaf lfunion4 { + type union { + type boolean; + type string; + } + } + + leaf lfunion5 { + type union { + type uint16; + type string; + } + } + + leaf lfunion6 { + type union { + type uint16; + type empty; + } + } + + leaf lfunion7 { + type tpdfun3; + } + + leaf lfunion8 { + type union { + type uint16; + type string; + } + } + + leaf lfunion9 { + type union { + type uint16; + type boolean; + } + } + + leaf lfunion10 { + type union { + type bits { + bit bt1; + bit bt2; + } + type boolean; + } + } + + leaf lfunion11 { + type union { + type tpdfun1; + type tpdfun2; + } + } + + leaf lfunion12 { + type tpdfun2; + } + + leaf lfunion13 { + type tpdfbit; + } + + leaf lfunion14 { + type union { + type enumeration { + enum zero; + enum one; + } + type uint16; + } + } + + leaf identityref1 { + type identityref { + base iden; + } + } + + + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/simple-data-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/simple-data-types.yang new file mode 100644 index 0000000..cf6e513 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/simple-data-types.yang @@ -0,0 +1,278 @@ +module simple-data-types { + namespace "simple:data:types"; + + prefix "smpdtp"; + revision 2013-11-12 { + } + + identity iden { + } + + typedef tpdfempty { + type empty; + } + + typedef tpdfbit { + type bits { + bit b1; + bit b2; + bit b3; + } + } + + typedef tpdfun4 { + type boolean; + } + + typedef tpdfun3 { + type union { + type tpdfbit; + type tpdfempty; + } + } + + typedef tpdfun2 { + type union { + type tpdfun3; + type tpdfun4; + } + } + + typedef tpdfun1 { + type union { + type uint8; + type decimal64 { + fraction-digits 2; + } + } + } + + container cont { + leaf lfnint8Min { + type int8; + } + leaf lfnint8Max { + type int8; + } + leaf lfnint16Min { + type int16; + } + leaf lfnint16Max { + type int16; + } + leaf lfnint32Min { + type int32; + } + leaf lfnint32Max { + type int32; + } + leaf lfnint64Min { + type int64; + } + leaf lfnint64Max { + type int64; + } + + leaf lfnuint8Max { + type uint8; + } + leaf lfnuint16Max { + type uint16; + } + leaf lfnuint32Max { + type uint32; + } + leaf lfuint64Max { + type uint64; + } + leaf lfstr { + type string; + } + leaf lfstr1 { + type string; + } + leaf lfbool1 { + type boolean; + } + leaf lfbool2 { + type boolean; + } + leaf lfbool3 { + type boolean; + } + leaf lfdecimal1 { + type decimal64 { + fraction-digits 2; + } + } + leaf lfdecimal2 { + type decimal64 { + fraction-digits 2; + } + } + leaf lfdecimal3 { + type decimal64 { + fraction-digits 2; + } + } + + leaf lfdecimal4 { + type decimal64 { + fraction-digits 2; + } + } + + + leaf lfdecimal6 { + type decimal64 { + fraction-digits 2; + } + } + + leaf lfenum { + type enumeration { + enum enum1; + enum enum2; + enum enum3; + enum enum4; + } + } + + leaf lfbits { + type bits { + bit bit1; + bit bit2; + bit bit3; + bit bit4; + } + } + + leaf lfbinary { + type binary; + } + + leaf lfref1 { //reference to string type + type leafref { + path "../lfstr"; + } + } + + leaf lfref2 { //reference to number type + type leafref { + path "../lfnint8Max"; + } + } + + leaf lfempty { + type empty; + } + + leaf lfunion1 { + type union { + type uint16; + type string; + } + } + leaf lfunion2 { + type union { + type decimal64 { + fraction-digits 2; + } + type string; + } + } + + leaf lfunion3 { + type union { + type empty; + type string; + } + } + + leaf lfunion4 { + type union { + type boolean; + type string; + } + } + + leaf lfunion5 { + type union { + type uint16; + type string; + } + } + + leaf lfunion6 { + type union { + type uint16; + type empty; + } + } + + leaf lfunion7 { + type tpdfun3; + } + + leaf lfunion8 { + type union { + type uint16; + type string; + } + } + + leaf lfunion9 { + type union { + type uint16; + type boolean; + } + } + + leaf lfunion10 { + type union { + type bits { + bit bt1; + bit bt2; + } + type boolean; + } + } + + leaf lfunion11 { + type union { + type tpdfun1; + type tpdfun2; + } + } + + leaf lfunion12 { + type tpdfun2; + } + + leaf lfunion13 { + type tpdfbit; + } + + leaf lfunion14 { + type union { + type enumeration { + enum zero; + enum one; + } + type uint16; + } + } + + leaf identityref1 { + type identityref { + base iden; + } + } + + anyxml complex-any; + + anyxml simple-any; + + anyxml empty-any; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/bad-data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/bad-data.xml new file mode 100644 index 0000000..31dfad1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/bad-data.xml @@ -0,0 +1,3 @@ +<cont xmlns= "simple:data:types"> + <lfnint8Min>invalid</lfnint8Min> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/data.xml new file mode 100644 index 0000000..86043d7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-data-types/xml/data.xml @@ -0,0 +1,71 @@ +<cont xmlns="simple:data:types"> + <lfnint8Min>-128</lfnint8Min> + <lfnint8Max>127</lfnint8Max> + <lfnint16Min>-32768</lfnint16Min> + <lfnint16Max>32767</lfnint16Max> + <lfnint32Min>-2147483648</lfnint32Min> + <lfnint32Max>2147483647</lfnint32Max> + <lfnint64Min>-9223372036854775808</lfnint64Min> + <lfnint64Max>9223372036854775807</lfnint64Max> + <lfnuint8Max>255</lfnuint8Max> + <lfnuint16Max>65535</lfnuint16Max> + <lfnuint32Max>4294967295</lfnuint32Max> + <lfstr>lfstr</lfstr> + <lfstr1></lfstr1> + <lfbool1>true</lfbool1> + <lfbool2>false</lfbool2> + <lfbool3>bla</lfbool3> + <lfdecimal1>43.32</lfdecimal1> + <lfdecimal2>-0.43</lfdecimal2> + <lfdecimal3>43</lfdecimal3> + <lfdecimal4>43E3</lfdecimal4> + <lfdecimal6>33.12345</lfdecimal6> + <lfenum>enum3</lfenum> + <lfbits>bit3 bit2</lfbits> + <lfbinary>ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</lfbinary> + <lfunion1>324</lfunion1> + <lfunion2>33.3</lfunion2> + <lfunion3>55</lfunion3> + <lfunion4>true</lfunion4> + <lfunion5>true</lfunion5> + <lfunion6>10</lfunion6> + <lfunion7></lfunion7> + <lfunion8></lfunion8> + <lfunion9></lfunion9> + <lfunion10>bt1</lfunion10> + <lfunion11>33</lfunion11> + <lfunion12>false</lfunion12> + <lfunion13>b1</lfunion13> + <lfunion14>zero</lfunion14> + <lfempty></lfempty> + <identityref1 xmlns:x="simple:data:types">x:iden</identityref1> + <complex-any> + <data> + <leaf1>leaf1-value</leaf1> + <leaf2>leaf2-value</leaf2> + + <leaf-list>leaf-list-value1</leaf-list> + <leaf-list>leaf-list-value2</leaf-list> + + <list> + <nested-list> + <nested-leaf>nested-value1</nested-leaf> + </nested-list> + <nested-list> + <nested-leaf>nested-value2</nested-leaf> + </nested-list> + </list> + + <list> + <nested-list> + <nested-leaf>nested-value3</nested-leaf> + </nested-list> + <nested-list> + <nested-leaf>nested-value4</nested-leaf> + </nested-list> + </list> + </data> + </complex-any> + <simple-any>simple</simple-any> + <empty-any></empty-any> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/simple-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/simple-yang-types.yang new file mode 100644 index 0000000..fdb3bfb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/simple-yang-types.yang @@ -0,0 +1,57 @@ +module simple-yang-types { + namespace "simple:yang:types"; + + prefix "smptp"; + revision 2013-11-5 { + description "Initial revision."; + } + + container cont1 { + leaf lf11 { + type string; + } + leaf-list lflst11 { + type int32; + } + leaf-list lflst12 { + type string; + } + list lst11 { + key "lf111"; + leaf lf111 { + type uint8; + } + leaf lf112 { + type string; + } + container cont111 { + leaf lf1111 { + type string; + } + leaf-list lflst1111 { + type int32; + } + list lst1111 { + leaf lf1111A { + type string; + } + leaf lf1111B { + type uint8; + } + } + } + list lst111 { + leaf lf1111 { + type int32; + } + } + list lst112 { + leaf lf1121 { + type string; + } + } + } + + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_data.json new file mode 100644 index 0000000..8888e7f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_data.json @@ -0,0 +1,72 @@ +{ + "cont1": { + "lf11":"lf", + "lflst11": [55,56,57], + "lflst12": ["lflst12 str1", "lflst12 str2", "lflst12 str3"], + "lst11": [ + { + "lf111":140, + "lf112":"lf112 str", + "cont111": { + "lf1111":"lf1111 str", + "lflst1111": [2048, 1024, 4096], + "lst1111": [ + { + "lf1111A": "lf1111A str11", + "lf1111B": 4 + }, + { + "lf1111A": "lf1111A str12", + "lf1111B": 7 + } + ] + }, + "lst111" : [ + { + "lf1111" : 65 + } + ], + "lst112" : [ + { + "lf1121" : "lf1121 str11" + } + ] + + }, + { + "lf111":141, + "lf112":"lf112 str2", + "cont111": { + "lf1111":"lf1111 str2", + "lflst1111": [2049, 1025, 4097], + "lst1111": [ + { + "lf1111A": "lf1111A str21", + "lf1111B": 5 + }, + { + "lf1111A": "lf1111A str22", + "lf1111B": 8 + } + ] + }, + "lst111" : [ + { + "lf1111" : 55 + }, + { + "lf1111" : 56 + } + ], + "lst112" : [ + { + "lf1121" : "lf1121 str21" + }, + { + "lf1121" : "lf1121 str22" + } + ] + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_empty_data.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_empty_data.json new file mode 100644 index 0000000..4b19988 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/awaited_output_empty_data.json @@ -0,0 +1,49 @@ +{ + "cont1": { + "lst11": [ + { + "lf111": 1, + "lst111": [ + { + "lf1111": 34 + }, + { + "lf1111": 35 + }, + {}, + {} + ], + "cont111": {} + }, + { + "lf111": 2, + "cont111": { + "lflst1111": [ + 1024, + 4096 + ], + "lst1111": [ + { + "lf1111B": 4 + }, + { + "lf1111A": "lf1111A str12" + } + ] + }, + "lst112": [ + {} + ] + }, + { + "lf111": 3, + "cont111": { + "lst1111": [ + {}, + {} + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/data.xml new file mode 100644 index 0000000..1df9ca5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/data.xml @@ -0,0 +1,63 @@ +<cont1 xmlns="simple:yang:types"> + <lf11>lf</lf11> + <lflst11>56</lflst11> + <lflst11>55</lflst11> + <lflst11>57</lflst11> + <lflst12>lflst12 str3</lflst12> + <lst11> + <lst112> + <lf1121>lf1121 str22</lf1121> + </lst112> + <lf111>141</lf111> + <lf112>lf112 str2</lf112> + <lst111> + <lf1111>55</lf1111> + </lst111> + <cont111> + <lflst1111>4097</lflst1111> + <lflst1111>2049</lflst1111> + <lflst1111>1025</lflst1111> + <lst1111> + <lf1111A>lf1111A str22</lf1111A> + <lf1111B>8</lf1111B> + </lst1111> + <lf1111>lf1111 str2</lf1111> + <lst1111> + <lf1111B>5</lf1111B> + <lf1111A>lf1111A str21</lf1111A> + </lst1111> + </cont111> + <lst111> + <lf1111>56</lf1111> + </lst111> + <lst112> + <lf1121>lf1121 str21</lf1121> + </lst112> + </lst11> + <lflst12>lflst12 str1</lflst12> + <lst11> + <lf111>140</lf111> + <lf112>lf112 str</lf112> + <cont111> + <lf1111>lf1111 str</lf1111> + <lflst1111>2048</lflst1111> + <lflst1111>1024</lflst1111> + <lflst1111>4096</lflst1111> + <lst1111> + <lf1111A>lf1111A str11</lf1111A> + <lf1111B>4</lf1111B> + </lst1111> + <lst1111> + <lf1111A>lf1111A str12</lf1111A> + <lf1111B>7</lf1111B> + </lst1111> + </cont111> + <lst111> + <lf1111>65</lf1111> + </lst111> + <lst112> + <lf1121>lf1121 str11</lf1121> + </lst112> + </lst11> + <lflst12>lflst12 str2</lflst12> +</cont1> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/empty_data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/empty_data.xml new file mode 100644 index 0000000..9cd503e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-json/simple-yang-types/xml/empty_data.xml @@ -0,0 +1,40 @@ +<cont1> + <lst11> + <lf111>1</lf111> + <lst111></lst111> + <lst111></lst111> + <lst111> + <lf1111></lf1111> + </lst111> + <lst111> + <lf1111>35</lf1111> + </lst111> + <cont111></cont111> + </lst11> + <lst11> + <lf111>2</lf111> + <cont111> + <lf1111></lf1111> + <lflst1111></lflst1111> + <lflst1111>1024</lflst1111> + <lflst1111>4096</lflst1111> + <lst1111> + <lf1111B>4</lf1111B> + </lst1111> + <lst1111> + <lf1111A>lf1111A str12</lf1111A> + </lst1111> + </cont111> + <lst112></lst112> + </lst11> + <lst11> + <lf111>3</lf111> + <cont111> + <lf1111></lf1111> + <lflst1111></lflst1111> + <lflst1111></lflst1111> + <lst1111></lst1111> + <lst1111></lst1111> + </cont111> + </lst11> +</cont1> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/choice/module-with-choice.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/choice/module-with-choice.yang new file mode 100644 index 0000000..8454784 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/choice/module-with-choice.yang @@ -0,0 +1,25 @@ +module module-with-choice { + namespace "module:with:choice"; + + prefix "mowicho"; + + revision 2013-12-18 { + } + + + container cont { + choice choA { + case caA1 { + leaf lf1 { + type string; + } + } + case caA2 { + leaf lf2 { + type string; + } + } + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module1.yang new file mode 100644 index 0000000..72a82ca --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module1.yang @@ -0,0 +1,20 @@ +module module1 { + namespace "module:one"; + + prefix "m1"; + revision 2014-01-17 { + } + + container cont_m1 { + leaf lf1_m1 { + type string; + } + uses confB_gr; + } + + grouping confB_gr { + container contB_m1 { + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module2.yang new file mode 100644 index 0000000..521d9c0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/data-of-several-modules/yang/module2.yang @@ -0,0 +1,20 @@ +module module2 { + namespace "module:two"; + + prefix "m2"; + revision 2014-01-17 { + } + + container cont_m2 { + leaf lf1_m2 { + type string; + } + uses confB_gr; + } + + grouping confB_gr { + container contB_m2 { + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/aug-referenced-elements-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/aug-referenced-elements-module.yang new file mode 100644 index 0000000..5829e04 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/aug-referenced-elements-module.yang @@ -0,0 +1,18 @@ +module aug-referenced-elements-module { + namespace "aug:referenced:elements:module"; + + prefix "augrefelmo"; + + import referenced-elements-module {prefix refelmo; revision-date 2013-12-03;} + + revision 2013-12-03 { + } + + augment "/refelmo:cont" { + leaf lf2 { + type boolean; + } + } + + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/eferenced-elements-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/eferenced-elements-module.yang new file mode 100644 index 0000000..a844ac6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/eferenced-elements-module.yang @@ -0,0 +1,20 @@ +module referenced-elements-module { + namespace "referenced:elements:module"; + + prefix "refelmo"; + + revision 2013-12-03 { + } + + container cont { + leaf lf1 { + type string; + } + } + leaf-list lflst1 { + type uint32; + } + + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/rinstance-identifier-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/rinstance-identifier-module.yang new file mode 100644 index 0000000..41a9001 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/instance_identifier/rinstance-identifier-module.yang @@ -0,0 +1,16 @@ +module instance-identifier-module { + namespace "instance:identifier:module"; + + prefix "inidmod"; + + revision 2013-12-03 { + } + + container cont { + leaf lf1 { + type instance-identifier { + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/basic-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/basic-module.yang new file mode 100644 index 0000000..efbac4d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/basic-module.yang @@ -0,0 +1,111 @@ +module basic-module { + namespace "basic:module"; + + prefix "basmod"; + + import referenced-module {prefix refmo; revision-date 2013-12-02;} + + revision 2013-12-02 { + } + + container cont { + container cont1 { + leaf lf11 { + type identityref { + base "refmo:iden"; + } + } + } + leaf lfStr { + type string; + } + leaf lfInt8 { + type int8; + } + + leaf lfInt16 { + type int16; + } + + leaf lfInt32 { + type int32; + } + + leaf lfInt64 { + type int64; + } + + leaf lfUint8 { + type uint8; + } + + leaf lfUint16 { + type uint16; + } + + leaf lfUint32 { + type uint32; + } + + leaf lfUint64 { + type uint64; + } + + leaf lfBinary { + type binary; + } + + leaf lfBits { + type bits { + bit one; + bit two; + bit three; + } + } + + leaf lfEnumeration { + type enumeration { + enum enum1; + enum enum2; + enum enum3; + } + } + + leaf lfEmpty { + type empty; + } + + leaf lfBoolean { + type boolean; + } + + leaf lfUnion { + type union { + type int8; + type string; + type bits { + bit first; + bit second; + } + type boolean; + } + } + + leaf lfLfref { + type leafref { + path "/cont/lfBoolean"; + } + } + + leaf lfLfrefNegative { + type leafref { + path "/cont/not-existing"; + } + } + + leaf lfInIdentifier { + type instance-identifier; + } + + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/referenced-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/referenced-module.yang new file mode 100644 index 0000000..e78266f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/nn-to-xml/yang/referenced-module.yang @@ -0,0 +1,10 @@ +module referenced-module { + namespace "referenced:module"; + + prefix "refmod"; + revision 2013-12-02 { + } + + identity iden { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/normalize-node/yang/normalize-node-module b/netconf/restconf/restconf-nb-bierman02/src/test/resources/normalize-node/yang/normalize-node-module new file mode 100644 index 0000000..15e68ef --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/normalize-node/yang/normalize-node-module @@ -0,0 +1,14 @@ +module normalize-node-module { + namespace "normalize:node:module"; + + prefix "nonomo"; + revision 2014-01-09 { + } + + container cont { + leaf lf1 { + type int32; + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/notifi-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/notifi-module.yang new file mode 100644 index 0000000..e9c0e4b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/notifi-module.yang @@ -0,0 +1,49 @@ +module notifi-module { + namespace "notifi:mod"; + prefix notm; + + revision "2016-11-23" { + } + + notification notifi-leaf { + leaf lf { + type string; + } + } + + notification notifi-cont { + container cont { + leaf lf { + type string; + } + } + } + + notification notifi-list { + list lst { + key lf; + leaf lf { + type string; + } + } + } + + notification notifi-grp { + uses grp; + } + + grouping grp { + leaf lf { + type string; + } + } + + notification notifi-augm { + } + + augment /notifi-augm { + leaf lf-augm { + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/subscribe-to-notification.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/subscribe-to-notification.yang new file mode 100644 index 0000000..5fe7df7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/subscribe-to-notification.yang @@ -0,0 +1,18 @@ +module subscribe-to-notification { + + yang-version 1; + namespace "subscribe:to:notification"; + prefix "subs-to-notifi"; + + description + "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream"; + + revision "2016-10-28" { + } + + container "notifi"{ + leaf "location"{ + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/toaster.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/toaster.yang new file mode 100644 index 0000000..da68016 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/toaster.yang @@ -0,0 +1,200 @@ +module toaster { + + yang-version 1; + + namespace + "http://netconfcentral.org/ns/toaster"; + + prefix toast; + + organization "Netconf Central"; + + contact + "Andy Bierman <andy@netconfcentral.org>"; + + description + "YANG version of the TOASTER-MIB."; + + revision "2009-11-20" { + description + "Toaster module in progress."; + } + + + identity toast-type { + description + "Base for all bread types supported by the toaster. + New bread types not listed here nay be added in the + future."; + } + + identity white-bread { + base toast:toast-type; + description "White bread."; + } + + identity wheat-bread { + base toast-type; + description "Wheat bread."; + } + + identity wonder-bread { + base toast-type; + description "Wonder bread."; + } + + identity frozen-waffle { + base toast-type; + description "Frozen waffle."; + } + + identity frozen-bagel { + base toast-type; + description "Frozen bagel."; + } + + identity hash-brown { + base toast-type; + description "Hash browned potatos."; + } + + typedef DisplayString { + type string { + length "0 .. 255"; + } + description + "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION."; + reference + "RFC 2579, section 2."; + + } + + container toaster { + presence + "Indicates the toaster service is available"; + description + "Top-level container for all toaster database objects."; + leaf toasterManufacturer { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's manufacturer. For instance, + Microsoft Toaster."; + } + + leaf toasterModelNumber { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's model. For instance, + Radiant Automatic."; + } + + leaf toasterStatus { + type enumeration { + enum "up" { + value 1; + description + "The toaster knob position is up. + No toast is being made now."; + } + enum "down" { + value 2; + description + "The toaster knob position is down. + Toast is being made now."; + } + } + config false; + mandatory true; + description + "This variable indicates the current state of + the toaster."; + } + + leaf darknessFactor { + type uint32; + config true; + default 1000; + description + "The darkness factor. Basically, the number of ms to multiple the doneness value by."; + } + } // container toaster + + rpc make-toast { + description + "Make some toast. + The toastDone notification will be sent when + the toast is finished. + An 'in-use' error will be returned if toast + is already being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + input { + leaf toasterDoneness { + type uint32 { + range "1 .. 10"; + } + default '5'; + description + "This variable controls how well-done is the + ensuing toast. It should be on a scale of 1 to 10. + Toast made at 10 generally is considered unfit + for human consumption; toast made at 1 is warmed + lightly."; + } + + leaf toasterToastType { + type identityref { + base toast:toast-type; + } + default 'wheat-bread'; + description + "This variable informs the toaster of the type of + material that is being toasted. The toaster + uses this information, combined with + toasterDoneness, to compute for how + long the material must be toasted to achieve + the required doneness."; + } + } + } // rpc make-toast + + rpc cancel-toast { + description + "Stop making toast, if any is being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + } // rpc cancel-toast + + rpc restock-toaster { + description + "Restocks the toaster with the amount of bread specified."; + + input { + leaf amountOfBreadToStock { + type uint32; + description + "Indicates the amount of bread to re-stock"; + } + } + } + + notification toasterOutOfBread { + description + "Indicates that the toaster has run of out bread."; + } // notification toasterOutOfStock + + notification toasterRestocked { + description + "Indicates that the toaster has run of out bread."; + leaf amountOfBread { + type uint32; + description + "Indicates the amount of bread that was re-stocked"; + } + } // notification toasterOutOfStock + + } // module toaster
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml new file mode 100644 index 0000000..43babd2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"> + <eventTime>2016-11-10T04:45:31+01:00</eventTime> + <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"> + <data-change-event> + <path>/toaster:toaster/toaster:toasterStatus</path> + <operation>updated</operation> + <data> + <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">down</toasterStatus> + </data> + </data-change-event> + </data-changed-notification> +</notification>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml new file mode 100644 index 0000000..8a4a866 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"> + <eventTime>2016-11-10T04:45:31+01:00</eventTime> + <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"> + <data-change-event> + <path>/toaster:toaster/toaster:toasterStatus</path> + <operation>updated</operation> + <data> + <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">1</toasterStatus> + </data> + </data-change-event> + </data-changed-notification> +</notification>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/ordered/by/user/ordered-by-user-example.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/ordered/by/user/ordered-by-user-example.yang new file mode 100644 index 0000000..2684336 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/ordered/by/user/ordered-by-user-example.yang @@ -0,0 +1,44 @@ +module ordered-example { + namespace "ordered:example"; + prefix "oex"; + + revision 2016-11-13 { + description + "Initial revision."; + } + + container cont { + list playlist { + key name; + + leaf name { + type string; + } + list song { + key index; + ordered-by user; + + leaf index { + type uint32; + } + leaf id { + type instance-identifier; + mandatory true; + description + "Song identifier. Must identify an instance of + /songs-cont/songs/song-name."; + } + } + } + } + + container songs-cont{ + list songs{ + key song-name; + + leaf song-name{ + type string; + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/invoke-rpc-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/invoke-rpc-module.yang new file mode 100644 index 0000000..7a8bcff --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/invoke-rpc-module.yang @@ -0,0 +1,26 @@ +module invoke-rpc-module { + namespace "invoke:rpc:module"; + prefix "inrpcmod"; + yang-version 1; + + revision 2017-05-23 { + description "Initial revision."; + } + + rpc rpc-test { + input { + container cont { + leaf lf { + type string; + } + } + } + output { + container cont-out { + leaf lf-out { + type string; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/mount-point.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/mount-point.yang new file mode 100644 index 0000000..02a468f --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/mount-point.yang @@ -0,0 +1,17 @@ +module mount-point { + namespace "mount:point"; + prefix "mountp"; + yang-version 1; + + import parser-identifier-included { prefix pii; revision-date 2016-06-02; } + + revision 2016-06-02 { + description "Initial revision."; + } + + container mount-container { + leaf point-number { + type uint8; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test-included.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test-included.yang new file mode 100644 index 0000000..d5200a9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test-included.yang @@ -0,0 +1,23 @@ +module parser-identifier-included { + namespace "parser:identifier:included"; + prefix "parseridinc"; + yang-version 1; + + revision 2016-06-02 { + description + "Initial revision."; + } + + list list-1 { + key "name revision"; + description "List in grouping"; + + leaf name { + type string; + } + + leaf revision { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test.yang new file mode 100644 index 0000000..ac91dc1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/parser-identifier-test.yang @@ -0,0 +1,40 @@ +module parser-identifier { + namespace "parser:identifier"; + prefix "parserid"; + yang-version 1; + + import parser-identifier-included { prefix pii; revision-date 2016-06-02; } + + revision 2016-06-02 { + description + "Initial revision."; + } + + container cont1 { + container cont2 { + list listTest { + uses group; + } + } + } + + grouping group { + list list-in-grouping { + key name; + + leaf name { + type string; + } + + leaf leaf-A.B { + type uint8; + } + } + } + + augment "/pii:list-1" { + leaf augment-leaf { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/test-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/test-module.yang new file mode 100644 index 0000000..a9a6756 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parser-identifier/test-module.yang @@ -0,0 +1,9 @@ +module test-module { + namespace "test:module"; + prefix "testm"; + yang-version 1; + + revision 2016-06-02 { + description "Initial revision."; + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.json new file mode 100644 index 0000000..0b39dc7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.json @@ -0,0 +1,10 @@ +{ + "interface":[ + { + "name":"eth0", + "type":"ethernetCsmacd", + "enabled":false, + "description": "some interface" + } + ] +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.xml new file mode 100644 index 0000000..19569b5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces.xml @@ -0,0 +1,6 @@ +<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + <description>some interface</description> +</interface> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces2.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces2.xml new file mode 100644 index 0000000..b4bdec8 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces2.xml @@ -0,0 +1,5 @@ +<class xmlns="test:module"> + <name>xxx</name> + <address>bbb</address> + <email>ccc</email> +</class>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.json new file mode 100644 index 0000000..7de7fac --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.json @@ -0,0 +1,12 @@ +{ + "ietf-interfaces:interfaces":{ + "interface":[ + { + "name":"eth0", + "type":"ethernetCsmacd", + "enabled":false, + "description": "some interface" + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.xml new file mode 100644 index 0000000..313f32d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path.xml @@ -0,0 +1,8 @@ +<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" > + <interface> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + <description>some interface</description> + </interface> +</interfaces>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path2.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path2.xml new file mode 100644 index 0000000..77cb026 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_absolute_path2.xml @@ -0,0 +1,7 @@ + +<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + <description>some interface</description> +</interface> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml new file mode 100644 index 0000000..19569b5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml @@ -0,0 +1,6 @@ +<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + <description>some interface</description> +</interface> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_patch.json b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_patch.json new file mode 100644 index 0000000..26b8f8d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/parts/ietf-interfaces_interfaces_patch.json @@ -0,0 +1,22 @@ +{ + "ietf-restconf:yang-patch" : { + "patch-id" : "0", + "edit" : [ + { + "edit-id" : "edit1", + "operation" : "create", + "target" : "", + "value" : { + "interface":[ + { + "name":"eth0", + "type":"ethernetCsmacd", + "enabled":"false", + "description": "some interface" + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-inet-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-inet-types.yang new file mode 100644 index 0000000..de20feb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-inet-types.yang @@ -0,0 +1,418 @@ + module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of protocol field related types ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code-Point + that may be used for marking packets in a traffic stream. + + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The flow-label type represents flow identifier or Flow Label + in an IPv6 packet header that may be used to discriminate + traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from <http://www.iana.org/>. + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of autonomous system related types ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASs'. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4893: BGP Support for Four-octet AS Number Space + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of IP address and hostname related types ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the compressed + format described in RFC 4291, Section 2.2, item 2 with the + following additional rules: the :: substitution must be + applied to the longest sequence of all-zero 16-bit chunks + in an IPv6 address. If there is a tie, the first sequence + of all-zero 16-bit chunks is replaced by ::. Single + all-zero 16-bit chunks are not compressed. The canonical + format uses lowercase characters and leading zeros are + not allowed. The canonical format for the zone index is + the numerical format as described in RFC 4007, Section + 11.2."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, IPv6 address is represented + in the compressed format described in RFC 4291, Section + 2.2, item 2 with the following additional rules: the :: + substitution must be applied to the longest sequence of + all-zero 16-bit chunks in an IPv6 address. If there is + a tie, the first sequence of all-zero 16-bit chunks is + replaced by ::. Single all-zero 16-bit chunks are not + compressed. The canonical format uses lowercase + characters and leading zeros are not allowed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitely or it may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 3492: Punycode: A Bootstring encoding of Unicode for + Internationalized Domain Names in Applications + (IDNA) + RFC 5891: Internationalizing Domain Names in Applications + (IDNA): Protocol"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf-monitoring@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf-monitoring@2017-01-26.yang new file mode 100644 index 0000000..55c3cb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf-monitoring@2017-01-26.yang @@ -0,0 +1,149 @@ +module ietf-restconf-monitoring { + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; + prefix "rcmon"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains monitoring information for the + RESTCONF protocol. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + container restconf-state { + config false; + description + "Contains RESTCONF protocol monitoring information."; + + container capabilities { + description + "Contains a list of protocol capability URIs."; + + leaf-list capability { + type inet:uri; + description + "A RESTCONF protocol capability URI."; + } + } + + container streams { + description + "Container representing the notification event streams + supported by the server."; + reference + "RFC 5277, Section 3.4, <streams> element."; + + list stream { + key name; + description + "Each entry describes an event stream supported by + the server."; + + leaf name { + type string; + description + "The stream name."; + reference + "RFC 5277, Section 3.4, <name> element."; + } + + leaf description { + type string; + description + "Description of stream content."; + reference + "RFC 5277, Section 3.4, <description> element."; + } + + leaf replay-support { + type boolean; + default false; + description + "Indicates if replay buffer is supported for this stream. + If 'true', then the server MUST support the 'start-time' + and 'stop-time' query parameters for this stream."; + reference + "RFC 5277, Section 3.4, <replaySupport> element."; + } + + leaf replay-log-creation-time { + when "../replay-support" { + description + "Only present if notification replay is supported."; + } + type yang:date-and-time; + description + "Indicates the time the replay log for this stream + was created."; + reference + "RFC 5277, Section 3.4, <replayLogCreationTime> + element."; + } + + list access { + key encoding; + min-elements 1; + description + "The server will create an entry in this list for each + encoding format that is supported for this stream. + The media type 'text/event-stream' is expected + for all event streams. This list identifies the + subtypes supported for this stream."; + + leaf encoding { + type string; + description + "This is the secondary encoding format within the + 'text/event-stream' encoding used by all streams. + The type 'xml' is supported for XML encoding. + The type 'json' is supported for JSON encoding."; + } + + leaf location { + type inet:uri; + mandatory true; + description + "Contains a URL that represents the entry point + for establishing notification delivery via + server-sent events."; + } + } + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf@2017-01-26.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf@2017-01-26.yang new file mode 100644 index 0000000..dc54388 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-restconf@2017-01-26.yang @@ -0,0 +1,279 @@ +module ietf-restconf { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; + prefix "rc"; + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + + Author: Andy Bierman + <mailto:andy@yumaworks.com> + + Author: Martin Bjorklund + <mailto:mbj@tail-f.com> + + Author: Kent Watsen + <mailto:kwatsen@juniper.net>"; + + description + "This module contains conceptual YANG specifications + for basic RESTCONF media type definitions used in + RESTCONF protocol messages. + + Note that the YANG definitions within this module do not + represent configuration data of any kind. + The 'restconf-media-type' YANG extension statement + provides a normative syntax for XML and JSON + message-encoding purposes. + + Copyright (c) 2017 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8040; see + the RFC itself for full legal notices."; + + revision 2017-01-26 { + description + "Initial revision."; + reference + "RFC 8040: RESTCONF Protocol."; + } + + extension yang-data { + argument name { + yin-element true; + } + description + "This extension is used to specify a YANG data template that + represents conceptual data defined in YANG. It is + intended to describe hierarchical data independent of + protocol context or specific message-encoding format. + Data definition statements within a yang-data extension + specify the generic syntax for the specific YANG data + template, whose name is the argument of the 'yang-data' + extension statement. + + Note that this extension does not define a media type. + A specification using this extension MUST specify the + message-encoding rules, including the content media type. + + The mandatory 'name' parameter value identifies the YANG + data template that is being defined. It contains the + template name. + + This extension is ignored unless it appears as a top-level + statement. It MUST contain data definition statements + that result in exactly one container data node definition. + An instance of a YANG data template can thus be translated + into an XML instance document, whose top-level element + corresponds to the top-level container. + + The module name and namespace values for the YANG module using + the extension statement are assigned to instance document data + conforming to the data definition statements within + this extension. + + The substatements of this extension MUST follow the + 'data-def-stmt' rule in the YANG ABNF. + + The XPath document root is the extension statement itself, + such that the child nodes of the document root are + represented by the data-def-stmt substatements within + this extension. This conceptual document is the context + for the following YANG statements: + + - must-stmt + - when-stmt + - path-stmt + - min-elements-stmt + - max-elements-stmt + - mandatory-stmt + - unique-stmt + - ordered-by + - instance-identifier data type + + The following data-def-stmt substatements are constrained + when used within a 'yang-data' extension statement. + + - The list-stmt is not required to have a key-stmt defined. + - The if-feature-stmt is ignored if present. + - The config-stmt is ignored if present. + - The available identity values for any 'identityref' + leaf or leaf-list nodes are limited to the module + containing this extension statement and the modules + imported into that module. + "; + } + + rc:yang-data yang-errors { + uses errors; + } + + rc:yang-data yang-api { + uses restconf; + } + + grouping errors { + description + "A grouping that contains a YANG container + representing the syntax and semantics of a + YANG Patch error report within a response message."; + + container errors { + description + "Represents an error report returned by the server if + a request results in an error."; + + list error { + description + "An entry containing information about one + specific error that occurred while processing + a RESTCONF request."; + reference + "RFC 6241, Section 4.3."; + + leaf error-type { + type enumeration { + enum transport { + description + "The transport layer."; + } + enum rpc { + description + "The rpc or notification layer."; + } + enum protocol { + description + "The protocol operation layer."; + } + enum application { + description + "The server application layer."; + } + } + mandatory true; + description + "The protocol layer where the error occurred."; + } + + leaf error-tag { + type string; + mandatory true; + description + "The enumerated error-tag."; + } + + leaf error-app-tag { + type string; + description + "The application-specific error-tag."; + } + + leaf error-path { + type instance-identifier; + description + "The YANG instance identifier associated + with the error node."; + } + + leaf error-message { + type string; + description + "A message describing the error."; + } + + anydata error-info { + description + "This anydata value MUST represent a container with + zero or more data nodes representing additional + error information."; + } + } + } + } + + grouping restconf { + description + "Conceptual grouping representing the RESTCONF + root resource."; + + container restconf { + description + "Conceptual container representing the RESTCONF + root resource."; + + container data { + description + "Container representing the datastore resource. + Represents the conceptual root of all state data + and configuration data supported by the server. + The child nodes of this container can be any data + resources that are defined as top-level data nodes + from the YANG modules advertised by the server in + the 'ietf-yang-library' module."; + } + + container operations { + description + "Container for all operation resources. + + Each resource is represented as an empty leaf with the + name of the RPC operation from the YANG 'rpc' statement. + + For example, the 'system-restart' RPC operation defined + in the 'ietf-system' module would be represented as + an empty leaf in the 'ietf-system' namespace. This is + a conceptual leaf and will not actually be found in + the module: + + module ietf-system { + leaf system-reset { + type empty; + } + } + + To invoke the 'system-restart' RPC operation: + + POST /restconf/operations/ietf-system:system-restart + + To discover the RPC operations supported by the server: + + GET /restconf/operations + + In XML, the YANG module namespace identifies the module: + + <system-restart + xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/> + + In JSON, the YANG module name identifies the module: + + { 'ietf-system:system-restart' : [null] } + "; + } + + leaf yang-library-version { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + config false; + mandatory true; + description + "Identifies the revision date of the 'ietf-yang-library' + module that is implemented by this RESTCONF server. + Indicates the year, month, and day in YYYY-MM-DD + numeric format."; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-library@2016-06-21.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-library@2016-06-21.yang new file mode 100644 index 0000000..bc466ee --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-library@2016-06-21.yang @@ -0,0 +1,208 @@ +module ietf-yang-library { + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-library"; + prefix "yanglib"; + import ietf-yang-types { + prefix yang; + } + import ietf-inet-types { + prefix inet; + } + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: <https://datatracker.ietf.org/wg/netconf/> + WG List: <mailto:netconf@ietf.org> + WG Chair: Mehmet Ersue + <mailto:mehmet.ersue@nsn.com> + WG Chair: Mahesh Jethanandani + <mailto:mjethanandani@gmail.com> + Editor: Andy Bierman + <mailto:andy@yumaworks.com> + Editor: Martin Bjorklund + <mailto:mbj@tail-f.com> + Editor: Kent Watsen + <mailto:kwatsen@juniper.net>"; + description + "This module contains monitoring information about the YANG + modules and submodules that are used within a YANG-based + server. + Copyright (c) 2016 IETF Trust and the persons identified as + authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + This version of this YANG module is part of RFC 7895; see + the RFC itself for full legal notices."; + revision 2016-06-21 { + description + "Initial revision."; + reference + "RFC 7895: YANG Module Library."; + } + /* + * Typedefs + */ + typedef revision-identifier { + type string { + pattern '\d{4}-\d{2}-\d{2}'; + } + description + "Represents a specific date in YYYY-MM-DD format."; + } + /* + * Groupings + */ + grouping module-list { + description + "The module data structure is represented as a grouping + so it can be reused in configuration or another monitoring + data structure."; + grouping common-leafs { + description + "Common parameters for YANG modules and submodules."; + leaf name { + type yang:yang-identifier; + description + "The YANG module or submodule name."; + } + leaf revision { + type union { + type revision-identifier; + type string { length 0; } + } + description + "The YANG module or submodule revision date. + A zero-length string is used if no revision statement + is present in the YANG module or submodule."; + } + } + grouping schema-leaf { + description + "Common schema leaf parameter for modules and submodules."; + leaf schema { + type inet:uri; + description + "Contains a URL that represents the YANG schema + resource for this module or submodule. + This leaf will only be present if there is a URL + available for retrieval of the schema for this entry."; + } + } + list module { + key "name revision"; + description + "Each entry represents one revision of one module + currently supported by the server."; + uses common-leafs; + uses schema-leaf; + leaf namespace { + type inet:uri; + mandatory true; + description + "The XML namespace identifier for this module."; + } + leaf-list feature { + type yang:yang-identifier; + description + "List of YANG feature names from this module that are + supported by the server, regardless of whether they are + defined in the module or any included submodule."; + } + list deviation { + key "name revision"; + description + "List of YANG deviation module names and revisions + used by this server to modify the conformance of + the module associated with this entry. Note that + the same module can be used for deviations for + multiple modules, so the same entry MAY appear + within multiple 'module' entries. + The deviation module MUST be present in the 'module' + list, with the same name and revision values. + The 'conformance-type' value will be 'implement' for + the deviation module."; + uses common-leafs; + } + leaf conformance-type { + type enumeration { + enum implement { + description + "Indicates that the server implements one or more + protocol-accessible objects defined in the YANG module + identified in this entry. This includes deviation + statements defined in the module. + For YANG version 1.1 modules, there is at most one + module entry with conformance type 'implement' for a + particular module name, since YANG 1.1 requires that, + at most, one revision of a module is implemented. + For YANG version 1 modules, there SHOULD NOT be more + than one module entry for a particular module name."; + } + enum import { + description + "Indicates that the server imports reusable definitions + from the specified revision of the module but does + not implement any protocol-accessible objects from + this revision. + Multiple module entries for the same module name MAY + exist. This can occur if multiple modules import the + same module but specify different revision dates in + the import statements."; + } + } + mandatory true; + description + "Indicates the type of conformance the server is claiming + for the YANG module identified by this entry."; + } + list submodule { + key "name revision"; + description + "Each entry represents one submodule within the + parent module."; + uses common-leafs; + uses schema-leaf; + } + } + } + /* + * Operational state data nodes + */ + container modules-state { + config false; + description + "Contains YANG module monitoring information."; + leaf module-set-id { + type string; + mandatory true; + description + "Contains a server-specific identifier representing + the current set of modules and submodules. The + server MUST change the value of this leaf if the + information represented by the 'module' list instances + has changed."; + } + uses module-list; + } + /* + * Notifications + */ + notification yang-library-change { + description + "Generated when the set of modules and submodules supported + by the server has changed."; + leaf module-set-id { + type leafref { + path "/yanglib:modules-state/yanglib:module-set-id"; + } + mandatory true; + description + "Contains the module-set-id value representing the + set of modules and submodules supported at the server at + the time the notification is generated."; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-types.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-types.yang new file mode 100644 index 0000000..c3f952c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/impl/ietf-yang-types.yang @@ -0,0 +1,417 @@ + module ietf-yang-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types"; + prefix "yang"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/netmod/> + WG List: <mailto:netmod@ietf.org> + + WG Chair: David Partain + <mailto:david.partain@ericsson.com> + + WG Chair: David Kessens + <mailto:david.kessens@nsn.com> + + Editor: Juergen Schoenwaelder + <mailto:j.schoenwaelder@jacobs-university.de>"; + + description + "This module contains a collection of generally useful derived + YANG data types. + + Copyright (c) 2010 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6021; see + the RFC itself for full legal notices."; + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of counter and gauge types ***/ + + typedef counter32 { + type uint32; + description + "The counter32 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter32 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter32 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter32. + + In the value set and its semantics, this type is equivalent + to the Counter32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter32 { + type yang:counter32; + default "0"; + description + "The zero-based-counter32 type represents a counter32 + that has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^32-1 (4294967295 decimal), when it + wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter32 textual convention of the SMIv2."; + reference + "RFC 4502: Remote Network Monitoring Management Information + Base Version 2"; + } + + typedef counter64 { + type uint64; + description + "The counter64 type represents a non-negative integer + that monotonically increases until it reaches a + maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Counters have no defined 'initial' value, and thus, a + single value of a counter has (in general) no information + content. Discontinuities in the monotonically increasing + value normally occur at re-initialization of the + management system, and at other times as specified in the + description of a schema node using this type. If such + other times can occur, for example, the creation of + a schema node of type counter64 at times other than + re-initialization, then a corresponding schema node + should be defined, with an appropriate type, to indicate + the last discontinuity. + + The counter64 type should not be used for configuration + schema nodes. A default statement SHOULD NOT be used in + combination with the type counter64. + + In the value set and its semantics, this type is equivalent + to the Counter64 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef zero-based-counter64 { + type yang:counter64; + default "0"; + description + "The zero-based-counter64 type represents a counter64 that + has the defined 'initial' value zero. + + A schema node of this type will be set to zero (0) on creation + and will thereafter increase monotonically until it reaches + a maximum value of 2^64-1 (18446744073709551615 decimal), + when it wraps around and starts increasing again from zero. + + Provided that an application discovers a new schema node + of this type within the minimum time to wrap, it can use the + 'initial' value as a delta. It is important for a management + station to be aware of this minimum time and the actual time + between polls, and to discard data if the actual time is too + long or there is no defined minimum time. + + In the value set and its semantics, this type is equivalent + to the ZeroBasedCounter64 textual convention of the SMIv2."; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + typedef gauge32 { + type uint32; + description + "The gauge32 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^32-1 (4294967295 decimal), and + the minimum value cannot be smaller than 0. The value of + a gauge32 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge32 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the Gauge32 type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef gauge64 { + type uint64; + description + "The gauge64 type represents a non-negative integer, which + may increase or decrease, but shall never exceed a maximum + value, nor fall below a minimum value. The maximum value + cannot be greater than 2^64-1 (18446744073709551615), and + the minimum value cannot be smaller than 0. The value of + a gauge64 has its maximum value whenever the information + being modeled is greater than or equal to its maximum + value, and has its minimum value whenever the information + being modeled is smaller than or equal to its minimum value. + If the information being modeled subsequently decreases + below (increases above) the maximum (minimum) value, the + gauge64 also decreases (increases). + + In the value set and its semantics, this type is equivalent + to the CounterBasedGauge64 SMIv2 textual convention defined + in RFC 2856"; + reference + "RFC 2856: Textual Conventions for Additional High Capacity + Data Types"; + } + + /*** collection of identifier related types ***/ + + typedef object-identifier { + type string { + pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))' + + '(\.(0|([1-9]\d*)))*'; + } + description + "The object-identifier type represents administratively + assigned names in a registration-hierarchical-name tree. + + Values of this type are denoted as a sequence of numerical + non-negative sub-identifier values. Each sub-identifier + value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers + are separated by single dots and without any intermediate + whitespace. + + The ASN.1 standard restricts the value space of the first + sub-identifier to 0, 1, or 2. Furthermore, the value space + of the second sub-identifier is restricted to the range + 0 to 39 if the first sub-identifier is 0 or 1. Finally, + the ASN.1 standard requires that an object identifier + has always at least two sub-identifier. The pattern + captures these restrictions. + + Although the number of sub-identifiers is not limited, + module designers should realize that there may be + implementations that stick with the SMIv2 limit of 128 + sub-identifiers. + + This type is a superset of the SMIv2 OBJECT IDENTIFIER type + since it is not restricted to 128 sub-identifiers. Hence, + this type SHOULD NOT be used to represent the SMIv2 OBJECT + IDENTIFIER type, the object-identifier-128 type SHOULD be + used instead."; + reference + "ISO9834-1: Information technology -- Open Systems + Interconnection -- Procedures for the operation of OSI + Registration Authorities: General procedures and top + arcs of the ASN.1 Object Identifier tree"; + } + + + + + typedef object-identifier-128 { + type object-identifier { + pattern '\d*(\.\d*){1,127}'; + } + description + "This type represents object-identifiers restricted to 128 + sub-identifiers. + + In the value set and its semantics, this type is equivalent + to the OBJECT IDENTIFIER type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef yang-identifier { + type string { + length "1..max"; + pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*'; + pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*'; + } + description + "A YANG identifier string as defined by the 'identifier' + rule in Section 12 of RFC 6020. An identifier must + start with an alphabetic character or an underscore + followed by an arbitrary sequence of alphabetic or + numeric characters, underscores, hyphens, or dots. + + A YANG identifier MUST NOT start with any possible + combination of the lowercase or uppercase character + sequence 'xml'."; + reference + "RFC 6020: YANG - A Data Modeling Language for the Network + Configuration Protocol (NETCONF)"; + } + + /*** collection of date and time related types ***/ + + typedef date-and-time { + type string { + pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?' + + '(Z|[\+\-]\d{2}:\d{2})'; + } + description + "The date-and-time type is a profile of the ISO 8601 + standard for representation of dates and times using the + Gregorian calendar. The profile is defined by the + date-time production in Section 5.6 of RFC 3339. + + The date-and-time type is compatible with the dateTime XML + schema type with the following notable exceptions: + + (a) The date-and-time type does not allow negative years. + + (b) The date-and-time time-offset -00:00 indicates an unknown + time zone (see RFC 3339) while -00:00 and +00:00 and Z all + represent the same time zone in dateTime. + + (c) The canonical format (see below) of data-and-time values + differs from the canonical format used by the dateTime XML + schema type, which requires all times to be in UTC using the + time-offset 'Z'. + + This type is not equivalent to the DateAndTime textual + convention of the SMIv2 since RFC 3339 uses a different + separator between full-date and full-time and provides + higher resolution of time-secfrac. + + The canonical format for date-and-time values with a known time + zone uses a numeric time zone offset that is calculated using + the device's configured known offset to UTC time. A change of + the device's offset to UTC time will cause date-and-time values + to change accordingly. Such changes might happen periodically + in case a server follows automatically daylight saving time + (DST) time zone offset changes. The canonical format for + date-and-time values with an unknown time zone (usually referring + to the notion of local time) uses the time-offset -00:00."; + reference + "RFC 3339: Date and Time on the Internet: Timestamps + RFC 2579: Textual Conventions for SMIv2 + XSD-TYPES: XML Schema Part 2: Datatypes Second Edition"; + } + + typedef timeticks { + type uint32; + description + "The timeticks type represents a non-negative integer that + represents the time, modulo 2^32 (4294967296 decimal), in + hundredths of a second between two epochs. When a schema + node is defined that uses this type, the description of + the schema node identifies both of the reference epochs. + + In the value set and its semantics, this type is equivalent + to the TimeTicks type of the SMIv2."; + reference + "RFC 2578: Structure of Management Information Version 2 (SMIv2)"; + } + + typedef timestamp { + type yang:timeticks; + description + "The timestamp type represents the value of an associated + timeticks schema node at which a specific occurrence happened. + The specific occurrence must be defined in the description + of any schema node defined using this type. When the specific + occurrence occurred prior to the last time the associated + timeticks attribute was zero, then the timestamp value is + zero. Note that this requires all timestamp values to be + reset to zero when the value of the associated timeticks + attribute reaches 497+ days and wraps around to zero. + + The associated timeticks schema node must be specified + in the description of any schema node using this type. + + In the value set and its semantics, this type is equivalent + to the TimeStamp textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of generic address types ***/ + + typedef phys-address { + type string { + pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?'; + } + description + "Represents media- or physical-level addresses represented + as a sequence octets, each octet represented by two hexadecimal + numbers. Octets are separated by colons. The canonical + representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the PhysAddress textual convention of the SMIv2."; + reference + "RFC 2579: Textual Conventions for SMIv2"; + } + + typedef mac-address { + type string { + pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}'; + } + description + "The mac-address type represents an IEEE 802 MAC address. + The canonical representation uses lowercase characters. + + In the value set and its semantics, this type is equivalent + to the MacAddress textual convention of the SMIv2."; + reference + "IEEE 802: IEEE Standard for Local and Metropolitan Area + Networks: Overview and Architecture + RFC 2579: Textual Conventions for SMIv2"; + } + + /*** collection of XML specific types ***/ + + typedef xpath1.0 { + type string; + description + "This type represents an XPATH 1.0 expression. + + When a schema node is defined that uses this type, the + description of the schema node MUST specify the XPath + context in which the XPath expression is evaluated."; + reference + "XPATH: XML Path Language (XPath) Version 1.0"; + } + + } diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test-included.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test-included.yang new file mode 100644 index 0000000..b46898c --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test-included.yang @@ -0,0 +1,22 @@ +module deserializer-test-included { + namespace "deserializer:test:included"; + prefix "dti"; + yang-version 1; + + revision 2016-06-06 { + description + "Initial revision."; + } + + list augmented-list { + key list-key; + + leaf list-key { + type uint16; + } + + leaf list-value { + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test.yang new file mode 100644 index 0000000..38f989e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/deserializer/deserializer-test.yang @@ -0,0 +1,90 @@ +module deserializer-test { + namespace "deserializer:test"; + prefix "dt"; + yang-version 1; + + import deserializer-test-included { prefix dti; revision-date 2016-06-06; } + + revision 2016-06-06 { + description + "Initial revision."; + } + + container contA { + leaf-list leaf-list-A { + type string; + } + + leaf leaf-A { + type string; + } + + list list-A { + key list-key; + + leaf list-key { + type uint8; + } + + leaf-list leaf-list-AA { + type string; + } + } + } + + leaf-list leaf-list-0 { + type boolean; + } + + leaf leaf-0 { + type string; + } + + list list-no-key { + leaf name { + type string; + } + + leaf number { + type uint8; + } + } + + list list-one-key { + key name; + + leaf name { + type string; + } + + leaf number { + type uint8; + } + } + + list list-multiple-keys { + key "name number enabled"; + + leaf name { + type string; + } + + leaf number { + type uint8; + } + + leaf enabled { + type boolean; + } + + leaf string-value { + type string; + } + } + + augment "/dti:augmented-list" { + leaf augmented-leaf { + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/list-test.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/list-test.yang new file mode 100644 index 0000000..981e6fe --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/list-test.yang @@ -0,0 +1,39 @@ +module list-test { + namespace "list:test"; + prefix "listt"; + + revision 2016-04-29 { + description + "Initial revision."; + } + + container top { + list list1 { + key "key1 key2 key3"; + leaf key1 { + type string; + } + leaf key2 { + type string; + } + leaf key3 { + type string; + } + list list2 { + key "key4 key5"; + leaf key4 { + type string; + } + leaf key5 { + type string; + } + leaf result { + type string; + } + } + } + leaf-list Y { + type uint32; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test-included.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test-included.yang new file mode 100644 index 0000000..c404aeb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test-included.yang @@ -0,0 +1,22 @@ +module serializer-test-included { + namespace "serializer:test:included"; + prefix "sti"; + yang-version 1; + + revision 2016-06-06 { + description + "Initial revision."; + } + + list augmented-list { + key list-key; + + leaf list-key { + type uint16; + } + + leaf list-value { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test.yang new file mode 100644 index 0000000..691e4dc --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/restconf/parser/serializer/serializer-test.yang @@ -0,0 +1,86 @@ +module serializer-test { + namespace "serializer:test"; + prefix "st"; + yang-version 1; + + import serializer-test-included { prefix sti; revision-date 2016-06-06; } + + revision 2016-06-06 { + description + "Initial revision."; + } + + container contA { + leaf-list leaf-list-A { + type string; + } + + leaf leaf-A { + type string; + } + + list list-A { + key list-key; + + leaf list-key { + type uint8; + } + + leaf-list leaf-list-AA { + type string; + } + } + } + + leaf-list leaf-list-0 { + type boolean; + } + + leaf leaf-0 { + type string; + } + + list list-no-key { + leaf name { + type string; + } + + leaf number { + type uint8; + } + } + + list list-one-key { + key name; + + leaf name { + type string; + } + + leaf number { + type uint8; + } + } + + list list-multiple-keys { + key "name number enabled"; + + leaf name { + type string; + } + + leaf number { + type uint8; + } + + leaf enabled { + type boolean; + } + } + + augment "/sti:augmented-list" { + leaf augmented-leaf { + type string; + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/sal-remote@2014-01-14.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/sal-remote@2014-01-14.yang new file mode 100644 index 0000000..0f6aebf --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/sal-remote@2014-01-14.yang @@ -0,0 +1,112 @@ +module sal-remote { + + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"; + prefix "sal-remote"; + + organization "Cisco Systems, Inc."; + contact "Martin Bobak <mbobak@cisco.com>"; + + description + "This module contains the definition of methods related to + sal remote model. + + Copyright (c)2013 Cisco Systems, Inc. All rights reserved. + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2014-01-14" { + description + "Initial revision"; + } + + + typedef q-name { + type string; + reference + "http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#QName"; + } + + rpc create-data-change-event-subscription { + input { + leaf path { + type instance-identifier; + description "Subtree path. "; + } + } + output { + leaf stream-name { + type string; + description "Notification stream name."; + } + } + } + + rpc create-data-change-event-subscription2 { + input { + leaf path2 { + type instance-identifier; + description "Subtree path. "; + } + } + output { + leaf stream-name2 { + type string; + description "Notification stream name."; + } + } + } + + notification data-changed-notification { + description "Data change notification."; + list data-change-event { + key path; + leaf path { + type instance-identifier; + } + leaf store { + type enumeration { + enum config; + enum operation; + } + } + leaf operation { + type enumeration { + enum created; + enum updated; + enum deleted; + } + } + anyxml data{ + description "DataObject "; + } + } + } + + rpc create-notification-stream { + input { + leaf-list notifications { + type q-name; + description "Notification QNames"; + } + } + output { + leaf notification-stream-identifier { + type string; + description "Unique notification stream identifier, in which notifications will be propagated"; + } + } + } + + rpc begin-transaction{ + output{ + anyxml data-modification-transaction{ + description "DataModificationTransaction xml"; + } + } + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/toaster.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/toaster.yang new file mode 100644 index 0000000..ad6b9b0 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/streams/toaster.yang @@ -0,0 +1,197 @@ + module toaster { + + yang-version 1; + + namespace + "http://netconfcentral.org/ns/toaster"; + + prefix toast; + + organization "Netconf Central"; + + contact + "Andy Bierman <andy@netconfcentral.org>"; + + description + "YANG version of the TOASTER-MIB."; + + revision "2009-11-20" { + description + "Toaster module in progress."; + } + + + identity toast-type { + description + "Base for all bread types supported by the toaster. + New bread types not listed here nay be added in the + future."; + } + + identity white-bread { + base toast:toast-type; + description "White bread."; + } + + identity wheat-bread { + base toast-type; + description "Wheat bread."; + } + + identity wonder-bread { + base toast-type; + description "Wonder bread."; + } + + identity frozen-waffle { + base toast-type; + description "Frozen waffle."; + } + + identity frozen-bagel { + base toast-type; + description "Frozen bagel."; + } + + identity hash-brown { + base toast-type; + description "Hash browned potatos."; + } + + typedef DisplayString { + type string { + length "0 .. 255"; + } + description + "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION."; + reference + "RFC 2579, section 2."; + + } + + container toaster { + presence + "Indicates the toaster service is available"; + description + "Top-level container for all toaster database objects."; + leaf toasterManufacturer { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's manufacturer. For instance, + Microsoft Toaster."; + } + + leaf toasterModelNumber { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's model. For instance, + Radiant Automatic."; + } + + leaf toasterStatus { + type enumeration { + enum "up" { + value 1; + description + "The toaster knob position is up. + No toast is being made now."; + } + enum "down" { + value 2; + description + "The toaster knob position is down. + Toast is being made now."; + } + } + config false; + mandatory true; + description + "This variable indicates the current state of + the toaster."; + } + } // container toaster + + rpc make-toast { + description + "Make some toast. + The toastDone notification will be sent when + the toast is finished. + An 'in-use' error will be returned if toast + is already being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + input { + leaf toasterDoneness { + type uint32 { + range "1 .. 10"; + } + default '5'; + description + "This variable controls how well-done is the + ensuing toast. It should be on a scale of 1 to 10. + Toast made at 10 generally is considered unfit + for human consumption; toast made at 1 is warmed + lightly."; + } + + leaf toasterToastType { + type identityref { + base toast:toast-type; + } + default 'wheat-bread'; + description + "This variable informs the toaster of the type of + material that is being toasted. The toaster + uses this information, combined with + toasterDoneness, to compute for how + long the material must be toasted to achieve + the required doneness."; + } + } + } // rpc make-toast + + rpc testOutput { + output { + leaf textOut { + type string; + } + } + } + + rpc cancel-toast { + description + "Stop making toast, if any is being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + } // rpc cancel-toast + + notification toastDone { + description + "Indicates that the toast in progress has completed."; + leaf toastStatus { + type enumeration { + enum "done" { + value 0; + description "The toast is done."; + } + enum "cancelled" { + value 1; + description + "The toast was cancelled."; + } + enum "error" { + value 2; + description + "The toaster service was disabled or + the toaster is broken."; + } + } + description + "Indicates the final toast status"; + } + } // notification toastDone + } // module toaster diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface.yang new file mode 100644 index 0000000..7502f85 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface.yang @@ -0,0 +1,30 @@ +module mount-interface { + yang-version 1; + namespace "urn:ietf:params:xml:ns:yang:mount-interface"; + prefix "sn"; + + description + "test file"; + + revision "2014-07-01" { + description + "Initial revision"; + reference "will be defined"; + } + + container interfaces { + list interface { + key "name"; + + leaf name { + type string; + } + leaf type { + type string; + } + leaf enabled { + type string; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface2.yang new file mode 100644 index 0000000..086d682 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/test-mount-point/yang/test-interface2.yang @@ -0,0 +1,27 @@ +module mount-interface2 { + yang-version 1; + namespace "urn:ietf:params:xml:ns:yang:mount-interface2"; + prefix "snn"; + + description + "test file"; + + revision "2014-08-01" { + description + "Initial revision"; + reference "will be defined"; + } + + container class { + list student { + key "name"; + + leaf name { + type string; + } + leaf age { + type string; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/block-data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/block-data.xml new file mode 100644 index 0000000..625b04d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/block-data.xml @@ -0,0 +1,4 @@ +<block xmlns="urn:ietf:params:xml:ns:yang:test-interface"> + <address>456</address> + <location>First</location> +</block>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/data2.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/data2.xml new file mode 100644 index 0000000..21712cb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/data2.xml @@ -0,0 +1,4 @@ +<student xmlns="urn:ietf:params:xml:ns:yang:test-interface2"> + <name>Vojtech</name> + <age>17</age> +</student>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface.xml new file mode 100644 index 0000000..755c8a9 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface.xml @@ -0,0 +1,8 @@ +<interfaces xmlns="urn:ietf:params:xml:ns:yang:test-interface"> + <interface> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> + </interface> +</interfaces> + diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface2.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface2.xml new file mode 100644 index 0000000..05db4a5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface2.xml @@ -0,0 +1,5 @@ +<interface> + <name>eth0</name> + <type>ethernetCsmacd</type> + <enabled>false</enabled> +</interface> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface3.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface3.xml new file mode 100644 index 0000000..e59ba17 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/xml/test-interface3.xml @@ -0,0 +1,6 @@ +<class xmlns="urn:ietf:params:xml:ns:yang:test-interface2"> + <student> + <name>Thomas</name> + <age>23</age> + </student> +</class> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang1/test-interface.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang1/test-interface.yang new file mode 100644 index 0000000..d0699e2 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang1/test-interface.yang @@ -0,0 +1,49 @@ +module test-interface { + yang-version 1; + namespace "urn:ietf:params:xml:ns:yang:test-interface"; + prefix "sn"; + + description + "test file"; + + revision "2014-07-01" { + description + "Initial revision"; + reference "will be defined"; + } + + container interfaces { + + container block { + + leaf address { + type string; + } + leaf location { + type string; + } + } + + list interface { + key "name"; + + list sub-interface { + key "sub-name"; + + leaf sub-name { + type string; + } + } + + leaf name { + type string; + } + leaf type { + type string; + } + leaf enabled { + type string; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang2/test-interface2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang2/test-interface2.yang new file mode 100644 index 0000000..13bc0eb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/test-config-data/yang2/test-interface2.yang @@ -0,0 +1,27 @@ +module test-interface2 { + yang-version 1; + namespace "urn:ietf:params:xml:ns:yang:test-interface2"; + prefix "snn"; + + description + "test file"; + + revision "2014-08-01" { + description + "Initial revision"; + reference "will be defined"; + } + + container class { + list student { + key "name"; + + leaf name { + type string; + } + leaf age { + type string; + } + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/varioustest/xmldata.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/varioustest/xmldata.xml new file mode 100644 index 0000000..5d45980 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/varioustest/xmldata.xml @@ -0,0 +1,5 @@ +<cont xmlns="generalnamespace"> + <cont1> + <lf1 xmlns:prefix="prefix:name" xmlns:prefix2="prefix2:name">/prefix:somepath1/prefix2:somepath2</lf1> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container-yang/data-container.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container-yang/data-container.yang new file mode 100644 index 0000000..b038eb1 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container-yang/data-container.yang @@ -0,0 +1,35 @@ +module data-container-yang { + namespace "data:container:yang"; + + prefix "dtconyg"; + revision 2013-11-19 { + } + + container cont { + leaf lf1 { + type string; + } + + leaf lf2 { + type string; + } + + leaf lf3 { + type empty; + } + + leaf-list lflst1 { + type string; + } + list lst1 { + leaf lf11 { + type string; + } + } + container cont1 { + leaf lf11 { + type uint8; + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container.xml new file mode 100644 index 0000000..841d351 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-container.xml @@ -0,0 +1,14 @@ +<cont> + <lf1>str0</lf1> + <lf2></lf2> + <lf3/> + <lflst1>121</lflst1> + <lflst1>131</lflst1> + <lflst1>str1</lflst1> + <lst1> + <lf11>str2</lf11> + </lst1> + <cont1> + <lf11>100</lf11> + </cont1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-container.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-container.yang new file mode 100644 index 0000000..3df3413 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-container.yang @@ -0,0 +1,28 @@ +module data-container-yang { + namespace "data:container:yang"; + + prefix "dtconyg"; + revision 2013-11-19 { + } + + container cont { + list lst1 { + leaf lf11 { + type string; + } + leaf-list lflst11 { + type string; + } + list lst11 { + leaf lf111 { + type string; + } + } + container cont11 { + leaf lf111 { + type uint8; + } + } + } + } +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-list.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-list.yang new file mode 100644 index 0000000..47e2a45 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list-yang/data-list.yang @@ -0,0 +1,22 @@ +module data-list-yang { + namespace "data:list:yang"; + + prefix "dtlstyg"; + + import data-container-yang { + prefix "dtconyg"; + revision-date 2013-11-19; + } + + + revision 2013-11-19 { + } + + + + augment "/dtconyg:cont" { + leaf lf1 { + type string; + } + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list.xml new file mode 100644 index 0000000..9c4256a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-list.xml @@ -0,0 +1,21 @@ +<cont> + <lst1> + <lf11>str0</lf11> + <lflst11>121</lflst11> + <lflst11>131</lflst11> + <lflst11>str1</lflst11> + <lst11> + <lf111>str2</lf111> + </lst11> + <cont11> + <lf111>100</lf111> + </cont11> + </lst1> + <lst1> + <lflst11>221</lflst11> + <cont11> + <lf111>100</lf111> + </cont11> + </lst1> + <lf1>lf1</lf1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-nmspc-in-attributes.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-nmspc-in-attributes.xml new file mode 100644 index 0000000..848c020 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-nmspc-in-attributes.xml @@ -0,0 +1,5 @@ +<cont xmlns:x="x:namespace" xmlns:y="y:namespace"> + <cont1 xmlns:z="z:namespace" xmlns:a="a:namespace" xmlns:b="b:namespace"> + <lf11 xmlns:c="identity:module">c:iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module1.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module1.yang new file mode 100644 index 0000000..f6a81ae --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module1.yang @@ -0,0 +1,16 @@ +module module1 { + namespace "module:one"; + + prefix "m1"; + revision 2014-01-17 { + } + + container cont_m1 { + leaf lf1_m1 { + type string; + } + } + container contB_m1 { + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module2.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module2.yang new file mode 100644 index 0000000..bdd8ece --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/data-of-several-modules/yang/module2.yang @@ -0,0 +1,17 @@ +module module2 { + namespace "module:two"; + + prefix "m2"; + revision 2014-01-17 { + } + + container cont_m2 { + leaf lf1_m2 { + type string; + } + } + container contB_m2 { + } + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/empty-data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/empty-data.xml new file mode 100644 index 0000000..7d62b9e --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/empty-data.xml @@ -0,0 +1,8 @@ +<cont> + <lf1></lf1> + <lflst1></lflst1> + <lflst1></lflst1> + <lst1> + <lf11></lf11> + </lst1> +</cont> diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identity-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identity-module.yang new file mode 100644 index 0000000..09a34c5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identity-module.yang @@ -0,0 +1,10 @@ +module identity-module { + namespace "identity:module"; + + prefix "idemod"; + revision 2013-12-02 { + } + + identity iden { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identityref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identityref-module.yang new file mode 100644 index 0000000..e0ef1bb --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/identityref-module.yang @@ -0,0 +1,21 @@ +module identityref-module { + namespace "identityref:module"; + + prefix "iderefmod"; + + import identity-module {prefix idemo; revision-date 2013-12-02;} + + revision 2013-12-02 { + } + + container cont { + container cont1 { + leaf lf11 { + type identityref { + base "idemo:iden"; + } + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-element.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-element.xml new file mode 100644 index 0000000..ebf7ac6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-element.xml @@ -0,0 +1,5 @@ +<cont xmlns="general:module" xmlns:x="x:namespace" xmlns:y="y:namespace"> + <cont1 xmlns:z="z:namespace" xmlns:a="a:namespace" xmlns:b="b:namespace"> + <lf11 xmlns="identityref:module" xmlns:c="c:namespace">iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-parrent-element.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-parrent-element.xml new file mode 100644 index 0000000..3fe1e4b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-default-nmspc-in-parrent-element.xml @@ -0,0 +1,5 @@ +<cont xmlns:x="x:namespace" xmlns:y="y:namespace"> + <cont1 xmlns="identityref:module" xmlns:z="z:namespace" xmlns:a="a:namespace" xmlns:b="b:namespace"> + <lf11 xmlns:c="c:namespace">iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-element.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-element.xml new file mode 100644 index 0000000..76de72d --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-element.xml @@ -0,0 +1,5 @@ +<cont xmlns="identityref:module" xmlns:x="x:namespace" xmlns:y="y:namespace"> + <cont1 xmlns:z="z:namespace" xmlns:a="a:namespace" xmlns:b="b:namespace"> + <lf11 xmlns:c="identity:module">c:iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-parrent-element.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-parrent-element.xml new file mode 100644 index 0000000..30a5418 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-nmspc-in-parrent-element.xml @@ -0,0 +1,5 @@ +<cont xmlns="identityref:module" xmlns:x="x:namespace" xmlns:y="y:namespace"> + <cont1 xmlns:c="identity:module" xmlns:z="z:namespace" xmlns:a="a:namespace" xmlns:b="b:namespace"> + <lf11>z:iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-with-prefix.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-with-prefix.xml new file mode 100644 index 0000000..7d31fa7 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-with-prefix.xml @@ -0,0 +1,5 @@ +<cont> + <cont1> + <lf11>x:iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-without-prefix.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-without-prefix.xml new file mode 100644 index 0000000..c65df1a --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/xml/data-no-nmspc-value-without-prefix.xml @@ -0,0 +1,5 @@ +<cont> + <cont1> + <lf11>iden</lf11> + </cont1> +</cont>
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/general-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/general-module.yang new file mode 100644 index 0000000..f1a1ea6 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/general-module.yang @@ -0,0 +1,14 @@ +module general-module { + namespace "general:module"; + + prefix "genmod"; + revision 2013-12-12 { + } + + container cont { + container cont1 { + } + } + + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identity-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identity-module.yang new file mode 100644 index 0000000..09a34c5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identity-module.yang @@ -0,0 +1,10 @@ +module identity-module { + namespace "identity:module"; + + prefix "idemod"; + revision 2013-12-02 { + } + + identity iden { + } +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identityref-module.yang b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identityref-module.yang new file mode 100644 index 0000000..8fd9002 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/identityref/yang-augments/identityref-module.yang @@ -0,0 +1,20 @@ +module identityref-module { + namespace "identityref:module"; + + prefix "iderefmod"; + + import identity-module {prefix idemo; revision-date 2013-12-02;} + import general-module {prefix gmo; revision-date 2013-12-12;} + + revision 2013-12-02 { + } + + augment "/gmo:cont/gmo:cont1" { + leaf lf11 { + type identityref { + base "idemo:iden"; + } + } + } + +} diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/leafref-module b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/leafref-module new file mode 100644 index 0000000..6fe770b --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/leafref-module @@ -0,0 +1,61 @@ +module leafref-module { + namespace "leafref:module"; + + prefix "lfrfmo"; + revision 2013-11-18 { + } + + identity base {} + + container cont { + leaf lf1 { + type int32; + } + leaf lf2 { + type leafref { + path "/cont/lf1"; + } + } + + leaf lf-ident { + type identityref { + base "lfrfmo:base"; + } + } + + leaf lf-ident-ref { + type leafref { + path "/cont/lf-ident"; + } + } + + leaf lf-ident-ref-relative { + type leafref { + path "../lf-ident"; + } + } + + leaf lf-ident-ref-relative-cnd { + type leafref { + path "/lfrfmo:cont/lfrfmo:lis[lfrfmo:id='abc']/lf-ident-ref"; + } + } + + + list lis { + key "id"; + + leaf id { + type string; + } + + leaf lf-ident-ref { + type leafref { + path "/cont/lf-ident"; + } + } + } + + } + +}
\ No newline at end of file diff --git a/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/xml/data.xml b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/xml/data.xml new file mode 100644 index 0000000..c3071e5 --- /dev/null +++ b/netconf/restconf/restconf-nb-bierman02/src/test/resources/xml-to-cnsn/leafref/xml/data.xml @@ -0,0 +1,8 @@ +<cont> + <lf1>121</lf1> + <lf2>121</lf2> + <lf-ident xmlns:a="leafref:module">a:base</lf-ident> + <lf-ident-ref xmlns:a="leafref:module">a:base</lf-ident-ref> + <lf-ident-ref-relative xmlns:a="leafref:module">a:base</lf-ident-ref-relative> + <lf-ident-ref-relative-cnd xmlns:a="leafref:module">a:base</lf-ident-ref-relative-cnd> +</cont>
\ No newline at end of file |