From a90a3ea4d95712c6023e18c6842551ebff1b6d1e Mon Sep 17 00:00:00 2001 From: rbrisuda Date: Wed, 10 Aug 2016 14:50:35 +0200 Subject: [PATCH] Bug 5615 - Netconf connector update overwriting existing topology data - putting only augmented node 'NetconfNode' to operational datastore - updated tests Change-Id: I3153613c9041c36f558e73128d40dd75dece0293 Signed-off-by: Rudolf Brisuda (cherry picked from commit de60ce77c83f1f09bb804f61124fe5c9f439c376) --- netconf/sal-netconf-connector/pom.xml | 5 + .../sal/NetconfDeviceTopologyAdapter.java | 17 +- .../sal/NetconfDeviceTopologyAdapterTest.java | 223 ++++++++- .../schemas/ietf-inet-types@2013-07-15.yang | 457 ++++++++++++++++++ .../schemas/netconf-node-topology.yang | 243 ++++++++++ ...work-topology-augment-test@2016-08-08.yang | 26 + .../schemas/network-topology@2013-10-21.yang | 339 +++++++++++++ .../src/test/resources/schemas/yang-ext.yang | 79 +++ 8 files changed, 1372 insertions(+), 17 deletions(-) create mode 100644 netconf/sal-netconf-connector/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang create mode 100644 netconf/sal-netconf-connector/src/test/resources/schemas/netconf-node-topology.yang create mode 100644 netconf/sal-netconf-connector/src/test/resources/schemas/network-topology-augment-test@2016-08-08.yang create mode 100644 netconf/sal-netconf-connector/src/test/resources/schemas/network-topology@2013-10-21.yang create mode 100644 netconf/sal-netconf-connector/src/test/resources/schemas/yang-ext.yang diff --git a/netconf/sal-netconf-connector/pom.xml b/netconf/sal-netconf-connector/pom.xml index 27fa1831b5..42e5914689 100644 --- a/netconf/sal-netconf-connector/pom.xml +++ b/netconf/sal-netconf-connector/pom.xml @@ -203,6 +203,11 @@ com.google.code.gson gson + + org.opendaylight.controller + sal-distributed-datastore + test + diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java index fd6519732b..b440b1d900 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java @@ -14,12 +14,10 @@ import com.google.common.collect.FluentIterable; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; - import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; - import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -122,13 +120,13 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { } public void updateDeviceData(boolean up, NetconfDeviceCapabilities capabilities) { - final Node data = buildDataForNetconfNode(up, capabilities); + final NetconfNode data = buildDataForNetconfNode(up, capabilities); final WriteTransaction writeTx = txChain.newWriteOnlyTransaction(); LOG.trace( "{}: Update device state transaction {} merging operational data started.", id, writeTx.getIdentifier()); - writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), data); + writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath().augmentation(NetconfNode.class), data, true); LOG.trace( "{}: Update device state transaction {} merging operational data ended.", id, writeTx.getIdentifier()); @@ -139,14 +137,13 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { public void setDeviceAsFailed(Throwable throwable) { String reason = (throwable != null && throwable.getMessage() != null) ? throwable.getMessage() : UNKNOWN_REASON; - final NetconfNode netconfNode = new NetconfNodeBuilder().setConnectionStatus(ConnectionStatus.UnableToConnect).setConnectedMessage(reason).build(); - final Node data = getNodeIdBuilder(id).addAugmentation(NetconfNode.class, netconfNode).build(); + final NetconfNode data = new NetconfNodeBuilder().setConnectionStatus(ConnectionStatus.UnableToConnect).setConnectedMessage(reason).build(); final WriteTransaction writeTx = txChain.newWriteOnlyTransaction(); LOG.trace( "{}: Setting device state as failed {} putting operational data started.", id, writeTx.getIdentifier()); - writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath(), data); + writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath().augmentation(NetconfNode.class), data, true); LOG.trace( "{}: Setting device state as failed {} putting operational data ended.", id, writeTx.getIdentifier()); @@ -154,7 +151,7 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { commitTransaction(writeTx, "update-failed-device"); } - private Node buildDataForNetconfNode(boolean up, NetconfDeviceCapabilities capabilities) { + private NetconfNode buildDataForNetconfNode(boolean up, NetconfDeviceCapabilities capabilities) { List capabilityList = new ArrayList<>(); capabilityList.addAll(capabilities.getNonModuleBasedCapabilities()); capabilityList.addAll(FluentIterable.from(capabilities.getResolvedCapabilities()).transform(AVAILABLE_CAPABILITY_TRANSFORMER).toList()); @@ -172,9 +169,7 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { .setAvailableCapabilities(avCapabalitiesBuilder.build()) .setUnavailableCapabilities(unavailableCapabilities); - final NodeBuilder nodeBuilder = getNodeIdBuilder(id); - - return nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build()).build(); + return netconfNodeBuilder.build(); } public void removeDeviceConfiguration() { diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapterTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapterTest.java index c3e4d0d4ac..2358819f90 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapterTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapterTest.java @@ -8,27 +8,79 @@ package org.opendaylight.netconf.sal.connect.netconf.sal; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import com.google.common.base.Optional; import com.google.common.util.concurrent.Futures; +import java.io.InputStream; import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javassist.ClassPool; +import org.custommonkey.xmlunit.XMLUnit; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.collections.Sets; +import org.opendaylight.controller.cluster.databroker.ConcurrentDOMDataBroker; +import org.opendaylight.controller.cluster.datastore.node.utils.AugmentationIdentifierGenerator; +import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter; +import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChain; import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeFields; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; public class NetconfDeviceTopologyAdapterTest { @@ -41,30 +93,89 @@ public class NetconfDeviceTopologyAdapterTest { @Mock private BindingTransactionChain txChain; @Mock - private Node data; + private NetconfNode data; private String txIdent = "test transaction"; + private SchemaContext schemaContext = null; + private String sessionIdForReporting = "netconf-test-session1"; + + private BindingTransactionChain transactionChain; + + private DataBroker dataBroker; + + private ConcurrentDOMDataBroker cDOMDataBroker; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); doReturn(txChain).when(broker).createTransactionChain(any(TransactionChainListener.class)); doReturn(writeTx).when(txChain).newWriteOnlyTransaction(); - doNothing().when(writeTx).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class)); - doNothing().when(writeTx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class)); + doNothing().when(writeTx).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(NetconfNode.class)); + doNothing().when(writeTx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(NetconfNode.class)); doReturn(txIdent).when(writeTx).getIdentifier(); + + this.schemaContext = parseYangStreams(getYangSchemas()); + schemaContext.getModules(); + final SchemaService schemaService = createSchemaService(); + + final DOMStore operStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", schemaService); + final DOMStore configStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", schemaService); + + final EnumMap datastores = new EnumMap<>(LogicalDatastoreType.class); + datastores.put(LogicalDatastoreType.CONFIGURATION, configStore); + datastores.put(LogicalDatastoreType.OPERATIONAL, operStore); + + ExecutorService listenableFutureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool( + 16, 16, "CommitFutures"); + + cDOMDataBroker = new ConcurrentDOMDataBroker(datastores, listenableFutureExecutor); + + final ClassPool pool = ClassPool.getDefault(); + final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool)); + final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + codecRegistry.onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext)); + + final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + final BindingToNormalizedNodeCodec bindingToNormalized = new BindingToNormalizedNodeCodec(loading, codecRegistry); + bindingToNormalized.onGlobalContextUpdated(schemaContext); + dataBroker = new BindingDOMDataBrokerAdapter(cDOMDataBroker, bindingToNormalized); + + transactionChain = dataBroker.createTransactionChain(new TransactionChainListener() { + @Override + public void onTransactionChainFailed(TransactionChain chain, AsyncTransaction transaction, Throwable cause) { + + } + + @Override + public void onTransactionChainSuccessful(TransactionChain chain) { + + } + }); + } @Test public void testFailedDevice() throws Exception { - doReturn(Futures.immediateCheckedFuture(null)).when(writeTx).submit(); + doReturn(Futures.immediateCheckedFuture(null)).when(writeTx).submit(); NetconfDeviceTopologyAdapter adapter = new NetconfDeviceTopologyAdapter(id, txChain); adapter.setDeviceAsFailed(null); verify(txChain, times(2)).newWriteOnlyTransaction(); - verify(writeTx, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class)); + verify(writeTx, times(1)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(NetconfNode.class)); + adapter.close(); + + adapter = new NetconfDeviceTopologyAdapter(id, transactionChain); //not a mock + adapter.setDeviceAsFailed(null); + + Optional netconfNode = dataBroker.newReadWriteTransaction().read(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath().augmentation(NetconfNode.class)).checkedGet(5, TimeUnit.SECONDS); + + assertEquals("Netconf node should be presented.", true, netconfNode.isPresent()); + assertEquals("Connection status should be failed.", NetconfNodeConnectionStatus.ConnectionStatus.UnableToConnect.getName(), netconfNode.get().getConnectionStatus().getName()); + } @Test @@ -75,7 +186,107 @@ public class NetconfDeviceTopologyAdapterTest { adapter.updateDeviceData(true, new NetconfDeviceCapabilities()); verify(txChain, times(2)).newWriteOnlyTransaction(); - verify(writeTx, times(2)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class)); + verify(writeTx, times(1)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(NetconfNode.class)); + verify(writeTx, times(1)).put(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(Node.class)); + + } + + @Test + public void testDeviceAugmentedNodePresence() throws Exception { + + Integer dataTestId = 474747; + + NetconfDeviceTopologyAdapter adapter = new NetconfDeviceTopologyAdapter(id, transactionChain); + + QName nTestLeafQname = QName.create("urn:TBD:params:xml:ns:yang:network-topology-augment-test", "2016-08-08", "test-id").intern(); + + YangInstanceIdentifier pathToAugmentedLeaf = YangInstanceIdentifier.builder().node(NetworkTopology.QNAME). + node(Topology.QNAME).nodeWithKey(Topology.QNAME, QName.create(Topology.QNAME, "topology-id"), "topology-netconf"). + node(Node.QNAME).nodeWithKey(Node.QNAME, QName.create(Node.QNAME, "node-id"), "test").node(nTestLeafQname).build(); + + NormalizedNode augmentNode = ImmutableLeafNodeBuilder.create().withValue(dataTestId).withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(nTestLeafQname)).build(); + + DOMDataWriteTransaction writeTx = cDOMDataBroker.newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.OPERATIONAL, pathToAugmentedLeaf, augmentNode); + writeTx.submit(); + + adapter.updateDeviceData(true, new NetconfDeviceCapabilities()); + Optional> testNode = cDOMDataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, pathToAugmentedLeaf).checkedGet(2, TimeUnit.SECONDS); + + assertEquals("Augmented node data should be still present after device update.", true, testNode.isPresent()); + assertEquals("Augmented data should be the same as before update node.", dataTestId, testNode.get().getValue()); + + adapter.setDeviceAsFailed(null); + testNode = cDOMDataBroker.newReadOnlyTransaction().read(LogicalDatastoreType.OPERATIONAL, pathToAugmentedLeaf).checkedGet(2, TimeUnit.SECONDS); + + assertEquals("Augmented node data should be still present after device failed.", true, testNode.isPresent()); + assertEquals("Augmented data should be the same as before failed device.", dataTestId, testNode.get().getValue()); + } + + private List getYangSchemas() { + final List schemaPaths = Arrays.asList("/schemas/network-topology@2013-10-21.yang", + "/schemas/ietf-inet-types@2013-07-15.yang", "/schemas/yang-ext.yang", "/schemas/netconf-node-topology.yang", + "/schemas/network-topology-augment-test@2016-08-08.yang"); + final List schemas = new ArrayList<>(); + + for (String schemaPath : schemaPaths) { + InputStream resourceAsStream = getClass().getResourceAsStream(schemaPath); + schemas.add(resourceAsStream); + } + + return schemas; + } + + private static SchemaContext parseYangStreams(final List streams) { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR + .newBuild(); + final SchemaContext schemaContext; + try { + schemaContext = reactor.buildEffective(streams); + } catch (ReactorException e) { + throw new RuntimeException("Unable to build schema context from " + streams, e); + } + return schemaContext; + } + + private SchemaService createSchemaService() { + return new SchemaService() { + + @Override + public void addModule(Module module) { + } + + @Override + public void removeModule(Module module) { + + } + + @Override + public SchemaContext getSessionContext() { + return schemaContext; + } + + @Override + public SchemaContext getGlobalContext() { + return schemaContext; + } + + @Override + public ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) { + listener.onGlobalContextUpdated(getGlobalContext()); + return new ListenerRegistration() { + @Override + public void close() { + + } + + @Override + public SchemaContextListener getInstance() { + return listener; + } + }; + } + }; } } \ No newline at end of file diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang new file mode 100644 index 0000000000..5c6f139a27 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang @@ -0,0 +1,457 @@ +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: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + 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 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - ipv6-address-no-zone"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of types related to protocol fields ***/ + + 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 ipv6-flow-label type represents the 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 . + + 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 types related to autonomous systems ***/ + + 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 ASes. 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 4001: Textual Conventions for Internet Network Addresses + RFC 6793: BGP Support for Four-Octet Autonomous System (AS) + Number Space"; + } + + /*** collection of types related to IP addresses and hostnames ***/ + + 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 representation + implies the IP version. This type supports scoped addresses + by allowing zone identifiers in the address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + 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 textual + representation defined in Section 4 of RFC 5952. The + canonical format for the zone index is the numerical + format as described in Section 11.2 of RFC 4007."; + 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-address-no-zone { + type union { + type inet:ipv4-address-no-zone; + type inet:ipv6-address-no-zone; + } + description + "The ip-address-no-zone type represents an IP address and is + IP version neutral. The format of the textual representation + implies the IP version. This type does not support scoped + addresses since it does not allow zone identifiers in the + address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address-no-zone { + type inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + 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 to 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, the IPv6 address is represented + as defined in Section 4 of RFC 5952."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** 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 + explicitly or 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 A-labels as per RFC 5890."; + 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 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + 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/sal-netconf-connector/src/test/resources/schemas/netconf-node-topology.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/netconf-node-topology.yang new file mode 100644 index 0000000000..62b88b974f --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/resources/schemas/netconf-node-topology.yang @@ -0,0 +1,243 @@ +module netconf-node-topology { + namespace "urn:opendaylight:netconf-node-topology"; + prefix "nettop"; + + import network-topology { prefix nt; revision-date 2013-10-21; } + import yang-ext { prefix ext; revision-date "2013-07-09";} + import ietf-inet-types { prefix inet; revision-date "2013-07-15"; } + + revision "2015-01-14" { + description "Initial revision of Topology model"; + } + + augment "/nt:network-topology/nt:topology/nt:topology-types" { + container topology-netconf { + } + } + + grouping netconf-node-credentials { + + choice credentials { + config true; + case login-password { + leaf username { + type string; + } + + leaf password { + type string; + } + } + } + } + + grouping netconf-node-connection-parameters { + + leaf host { + type inet:host; + } + + leaf port { + type inet:port-number; + } + + leaf tcp-only { + config true; + type boolean; + } + + leaf schemaless { + type boolean; + default false; + } + + container yang-module-capabilities { + config true; + leaf override { + type boolean; + default false; + description "Whether to override or merge this list of capabilities with capabilities from device"; + } + + leaf-list capability { + type string; + description "Set a list of capabilities to override capabilities provided in device's hello message. + Can be used for devices that do not report any yang modules in their hello message"; + } + } + + leaf reconnect-on-changed-schema { + config true; + type boolean; + default false; + description "If true, the connector would auto disconnect/reconnect when schemas are changed in the remote device. + The connector subscribes (right after connect) to base netconf notifications and listens for netconf-capability-change notification"; + } + + leaf connection-timeout-millis { + description "Specifies timeout in milliseconds after which connection must be established."; + config true; + type uint32; + default 20000; + } + + leaf default-request-timeout-millis { + description "Timeout for blocking operations within transactions."; + config true; + type uint32; + default 60000; + } + + leaf max-connection-attempts { + description "Maximum number of connection retries. Non positive value or null is interpreted as infinity."; + config true; + type uint32; + default 0; // retry forever + } + + leaf between-attempts-timeout-millis { + description "Initial timeout in milliseconds to wait between connection attempts. Will be multiplied by sleep-factor with every additional attempt"; + config true; + type uint16; + default 2000; + } + + leaf sleep-factor { + config true; + type decimal64 { + fraction-digits 1; + } + default 1.5; + } + + // Keepalive configuration + leaf keepalive-delay { + config true; + type uint32; + default 120; + description "Netconf connector sends keepalive RPCs while the session is idle, this delay specifies the delay between keepalive RPC in seconds + If a value <1 is provided, no keepalives will be sent"; + } + + leaf concurrent-rpc-limit { + config true; + type uint16; + default 0; + description "Limit of concurrent messages that can be send before reply messages are received. + If value <1 is provided, no limit will be enforced"; + } + } + + grouping netconf-node-connection-status { + + leaf connection-status { + config false; + type enumeration { + enum connecting; + enum connected; + enum unable-to-connect; + } + } + + container clustered-connection-status { + config false; + list node-status { + leaf node { + type string; + } + leaf status { + type enumeration { + enum connected; + enum unavailable; + enum failed; + } + } + } + } + + leaf connected-message { + config false; + type string; + } + + container available-capabilities { + config false; + leaf-list available-capability { + type string; + } + } + + container unavailable-capabilities { + config false; + list unavailable-capability { + leaf capability { + type string; + } + + leaf failure-reason { + type enumeration { + enum missing-source; + enum unable-to-resolve; + } + } + } + } + + container pass-through { + when "../connection-status = connected"; + description + "When the underlying node is connected, its NETCONF context + is available verbatim under this container through the + mount extension."; + } + + } + + grouping netconf-schema-storage { + leaf schema-cache-directory { + config true; + type string; + default "schema"; + description "The destination schema repository for yang files relative to the cache directory. This may be specified per netconf mount + so that the loaded yang files are stored to a distinct directory to avoid potential conflict."; + } + + container yang-library { + leaf yang-library-url { + config true; + type inet:uri; + description "Yang library to be plugged as additional source provider into the shared schema repository"; + } + + // credentials for basic http authentication + leaf username { + config true; + type string; + } + + leaf password { + config true; + type string; + } + } + } + + grouping netconf-node-fields { + + uses netconf-node-credentials; + + uses netconf-node-connection-parameters; + + uses netconf-node-connection-status; + + uses netconf-schema-storage; + + } + + augment "/nt:network-topology/nt:topology/nt:node" { + when "../../nt:topology-types/topology-netconf"; + ext:augment-identifier "netconf-node"; + + uses netconf-node-fields; + } +} diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology-augment-test@2016-08-08.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology-augment-test@2016-08-08.yang new file mode 100644 index 0000000000..b0f6925275 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology-augment-test@2016-08-08.yang @@ -0,0 +1,26 @@ +module network-topology-network-topology-augment-test { + + namespace "urn:TBD:params:xml:ns:yang:network-topology-augment-test"; + import yang-ext { prefix ext; revision-date "2013-07-09";} + + import network-topology{ + prefix "nt"; + revision-date 2013-10-21; + } + prefix "ntat"; + + description + "This module defines test model for the topology of a network with augmented node"; + + revision 2016-08-08; + + augment /nt:network-topology/nt:topology/nt:node { + leaf test-id { + type uint16; + } + leaf test-name { + type string; + } + } + +} diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology@2013-10-21.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology@2013-10-21.yang new file mode 100644 index 0000000000..dc5dcad708 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/resources/schemas/network-topology@2013-10-21.yang @@ -0,0 +1,339 @@ +module network-topology { + yang-version 1; + namespace "urn:TBD:params:xml:ns:yang:network-topology"; + // replace with IANA namespace when assigned + prefix "nt"; + + import ietf-inet-types { prefix "inet"; revision-date 2013-07-15; } + + organization "TBD"; + + contact "WILL-BE-DEFINED-LATER"; + + description + "This module defines a model for the topology of a network. + Key design decisions are as follows: + A topology consists of a set of nodes and links. + Links are point-to-point and unidirectional. + Bidirectional connections need to be represented through + two separate links. + Multipoint connections, broadcast domains etc can be represented + through a hierarchy of nodes, then connecting nodes at + upper layers of the hierarchy."; + + revision 2013-10-21 { + description + "Initial revision."; + } + + typedef topology-id { + type inet:uri; + description + "An identifier for a topology."; + } + + typedef node-id { + type inet:uri; + description + "An identifier for a node in a topology. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same node in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of node + and/or the type of topology that the node is a part of."; + } + + + typedef link-id { + type inet:uri; + description + "An identifier for a link in a topology. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same link in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of link + and/or the type of topology that the link is a part of."; + } + + typedef tp-id { + type inet:uri; + description + "An identifier for termination points on a node. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same TP in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of TP + and/or the type of node and topology that the TP is a part of."; + } + + typedef tp-ref { + type leafref { + path "/network-topology/topology/node/termination-point/tp-id"; + } + description + "A type for an absolute reference to a termination point. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + typedef topology-ref { + type leafref { + path "/network-topology/topology/topology-id"; + } + description + "A type for an absolute reference a topology instance."; + } + + typedef node-ref { + type leafref { + path "/network-topology/topology/node/node-id"; + } + description + + "A type for an absolute reference to a node instance. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + + typedef link-ref { + type leafref { + path "/network-topology/topology/link/link-id"; + } + description + "A type for an absolute reference a link instance. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + + grouping tp-attributes { + description + "The data objects needed to define a termination point. + (This only includes a single leaf at this point, used + to identify the termination point.) + Provided in a grouping so that in addition to the datastore, + the data can also be included in notifications."; + leaf tp-id { + type tp-id; + } + leaf-list tp-ref { + type tp-ref; + config false; + description + "The leaf list identifies any termination points that the + termination point is dependent on, or maps onto. + Those termination points will themselves be contained + in a supporting node. + This dependency information can be inferred from + the dependencies between links. For this reason, + this item is not separately configurable. Hence no + corresponding constraint needs to be articulated. + The corresponding information is simply provided by the + implementing system."; + } + } + + grouping node-attributes { + description + "The data objects needed to define a node. + The objects are provided in a grouping so that in addition to + the datastore, the data can also be included in notifications + as needed."; + + leaf node-id { + type node-id; + description + "The identifier of a node in the topology. + A node is specific to a topology to which it belongs."; + } + list supporting-node { + description + "This list defines vertical layering information for nodes. + It allows to capture for any given node, which node (or nodes) + in the corresponding underlay topology it maps onto. + A node can map to zero, one, or more nodes below it; + accordingly there can be zero, one, or more elements in the list. + If there are specific layering requirements, for example + specific to a particular type of topology that only allows + for certain layering relationships, the choice + below can be augmented with additional cases. + A list has been chosen rather than a leaf-list in order + to provide room for augmentations, e.g. for + statistics or priorization information associated with + supporting nodes."; + // This is not what was published in the initial draft, + // added topology-ref leaf and added it to the key + key "topology-ref node-ref"; + leaf topology-ref { + type topology-ref; + } + leaf node-ref { + type node-ref; + } + } + } + + grouping link-attributes { + // This is a grouping, not defined inline with the link definition itself, + // so it can be included in a notification, if needed + leaf link-id { + type link-id; + description + "The identifier of a link in the topology. + A link is specific to a topology to which it belongs."; + } + container source { + leaf source-node { + mandatory true; + type node-ref; + description + "Source node identifier, must be in same topology."; + } + leaf source-tp { + type tp-ref; + description + "Termination point within source node that terminates the link."; + + } + } + container destination { + leaf dest-node { + mandatory true; + type node-ref; + description + "Destination node identifier, must be in same topology."; + } + leaf dest-tp { + type tp-ref; + description + "Termination point within destination node that terminates the link."; + } + } + list supporting-link { + key "link-ref"; + leaf link-ref { + type link-ref; + } + } + } + + + container network-topology { + list topology { + description " + This is the model of an abstract topology. + A topology contains nodes and links. + Each topology MUST be identified by + unique topology-id for reason that a network could contain many + topologies. + "; + key "topology-id"; + leaf topology-id { + type topology-id; + description " + It is presumed that a datastore will contain many topologies. To + distinguish between topologies it is vital to have UNIQUE + topology identifiers. + "; + } + leaf server-provided { + type boolean; + config false; + description " + Indicates whether the topology is configurable by clients, + or whether it is provided by the server. This leaf is + + populated by the server implementing the model. + It is set to false for topologies that are created by a client; + it is set to true otherwise. If it is set to true, any + attempt to edit the topology MUST be rejected. + "; + } + container topology-types { + description + "This container is used to identify the type, or types + (as a topology can support several types simultaneously), + of the topology. + Topology types are the subject of several integrity constraints + that an implementing server can validate in order to + maintain integrity of the datastore. + Topology types are indicated through separate data nodes; + the set of topology types is expected to increase over time. + To add support for a new topology, an augmenting module + needs to augment this container with a new empty optional + container to indicate the new topology type. + The use of a container allows to indicate a subcategorization + of topology types. + The container SHALL NOT be augmented with any data nodes + that serve a purpose other than identifying a particular + topology type. + "; + } + list underlay-topology { + key "topology-ref"; + leaf topology-ref { + type topology-ref; + } + // a list, not a leaf-list, to allow for potential augmentation + // with properties specific to the underlay topology, + // such as statistics, preferences, or cost. + description + "Identifies the topology, or topologies, that this topology + is dependent on."; + } + + list node { + description "The list of network nodes defined for the topology."; + key "node-id"; + uses node-attributes; + must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])"; + // This constraint is meant to ensure that a referenced node is in fact + // a node in an underlay topology. + list termination-point { + description + + "A termination point can terminate a link. + Depending on the type of topology, a termination point could, + for example, refer to a port or an interface."; + key "tp-id"; + uses tp-attributes; + } + } + + list link { + description " + A Network Link connects a by Local (Source) node and + a Remote (Destination) Network Nodes via a set of the + nodes' termination points. + As it is possible to have several links between the same + source and destination nodes, and as a link could potentially + be re-homed between termination points, to ensure that we + would always know to distinguish between links, every link + is identified by a dedicated link identifier. + Note that a link models a point-to-point link, not a multipoint + link. + Layering dependencies on links in underlay topologies are + not represented as the layering information of nodes and of + termination points is sufficient. + "; + key "link-id"; + uses link-attributes; + must "boolean(../underlay-topology/link[./supporting-link])"; + // Constraint: any supporting link must be part of an underlay topology + must "boolean(../node[./source/source-node])"; + // Constraint: A link must have as source a node of the same topology + must "boolean(../node[./destination/dest-node])"; + // Constraint: A link must have as source a destination of the same topology + must "boolean(../node/termination-point[./source/source-tp])"; + // Constraint: The source termination point must be contained in the source node + must "boolean(../node/termination-point[./destination/dest-tp])"; + // Constraint: The destination termination point must be contained + // in the destination node + } + } + } +} diff --git a/netconf/sal-netconf-connector/src/test/resources/schemas/yang-ext.yang b/netconf/sal-netconf-connector/src/test/resources/schemas/yang-ext.yang new file mode 100644 index 0000000000..0fbe94ddc4 --- /dev/null +++ b/netconf/sal-netconf-connector/src/test/resources/schemas/yang-ext.yang @@ -0,0 +1,79 @@ +module yang-ext { + yang-version 1; + namespace "urn:opendaylight:yang:extension:yang-ext"; + prefix "ext"; + + contact "Anton Tkacik "; + + description + "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"; + + revision "2013-07-09" { + description ""; + } + + // Augmentation name + + extension "augment-identifier" { + description + "YANG language extension which assigns an identifier to + augmentation. Augment identifier is used to identify + specific augment statement by name. + + The identifier syntax is defined formally defined by the rule + 'identifier' in Section 12 of RFC 6020. + + All augment identifiers defined in a namespace MUST be unique. + The namespace of augment identifiers is shared by module and + its submodules."; + + /* + Discussion: + This extension allows for ease of development / debug + of YANG modules and it is suitable for code generation, + where each augment statement is nicely identified by + unique name instead of combination of augment target + and when condition. + */ + argument "identifier"; + } + + + // Context-aware RPCs + + grouping rpc-context-ref { + description + "A reference to RPC context."; + leaf context-instance { + type instance-identifier; + description "Pointer to the context. "; + } + } + + extension "rpc-context-instance" { + description + "YANG language extension which defines enclosing (parent) + schema node as referencable context for RPCs. + + The argument is identity which is used to identify RPC context + type."; + + argument "context-type"; + } + + extension "context-reference" { + argument "context-type"; + } + + extension "context-instance" { + argument "context-type"; + } + + extension "instance-target" { + argument "path"; + } +} -- 2.36.6