<artifactId>netty-common</artifactId>
<version>${netty.version}</version>
</dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http</artifactId>
+ <version>${netty.version}</version>
+ </dependency>
<!-- yangtools dependencies -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http</artifactId>
+ </dependency>
<!-- testing dependencies I'm pretty sure we should trim -->
<dependency>
return new IPHostId(addr);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("IP=[");
+ if (this.ipAddress != null) {
+ builder.append(this.ipAddress.getHostAddress());
+ }
+ builder.append("]");
+ return (builder.toString());
+ }
}
return new IPMacHostId(ip, mac);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("IP=[");
+ if (this.ipAddress != null) {
+ builder.append(this.ipAddress.getHostAddress());
+ }
+ builder.append("]")
+ .append("MAC=[");
+ if (this.macAddr != null) {
+ builder.append(this.macAddr.toString());
+ }
+ builder.append("]");
+ return (builder.toString());
+ }
}
}
}
+ /*
+ * This thread runs every 4 seconds
+ */
+
class OutStandingARPHandler extends TimerTask {
@Override
public void run() {
return;
}
ARPPending arphost;
- /* This routine runs every 4 seconds */
- logger.trace("Number of Entries in ARP Pending/Failed Lists: ARPPendingList = {}, failedARPReqList = {}",
- ARPPendingList.size(), failedARPReqList.size());
- for (Entry<IHostId, ARPPending> entry : ARPPendingList.entrySet()) {
- arphost = entry.getValue();
-
- if (hostsDB.containsKey(arphost.getHostId())) {
- // this host is already learned, shouldn't be in
- // ARPPendingList
- // Remove it and continue
- logger.warn("Learned Host {} found in ARPPendingList", decodeIPFromId(arphost.getHostId()));
- ARPPendingList.remove(entry.getKey());
- continue;
- }
- if (arphost.getSent_count() < hostRetryCount) {
- /*
- * No reply has been received of first ARP Req, send the
- * next one. Before sending the ARP, check if ARPHandler is
- * available or not
- */
- if (hostFinder == null) {
- logger.warn("ARPHandler Services are not available for Outstanding ARPs");
+ try {
+ for (Entry<IHostId, ARPPending> entry : ARPPendingList.entrySet()) {
+ arphost = entry.getValue();
+
+ if (hostsDB.containsKey(arphost.getHostId())) {
+ // this host is already learned, shouldn't be in
+ // ARPPendingList
+ // Remove it and continue
+ logger.warn("Learned Host {} found in ARPPendingList", decodeIPFromId(arphost.getHostId()));
+ ARPPendingList.remove(entry.getKey());
continue;
}
- for (IHostFinder hf : hostFinder) {
- hf.find(decodeIPFromId(arphost.getHostId()));
- }
- arphost.sent_count++;
- logger.debug("ARP Sent from ARPPending List, IP: {}", decodeIPFromId(arphost.getHostId()));
- } else if (arphost.getSent_count() >= hostRetryCount) {
- /*
- * ARP requests have been sent without receiving a reply,
- * remove this from the pending list
- */
- ARPPendingList.remove(entry.getKey());
- logger.debug("ARP reply not received after multiple attempts, removing from Pending List IP: {}",
- decodeIPFromId(arphost.getHostId()));
- /*
- * Add this host to a different list which will be processed
- * on link up events
- */
- logger.debug("Adding the host to FailedARPReqList IP: {}", decodeIPFromId(arphost.getHostId()));
- failedARPReqList.put(entry.getKey(), arphost);
+ if (arphost.getSent_count() < hostRetryCount) {
+ /*
+ * No reply has been received of first ARP Req, send the
+ * next one. Before sending the ARP, check if ARPHandler
+ * is available or not
+ */
+ if (hostFinder == null) {
+ logger.warn("ARPHandler Services are not available for Outstanding ARPs");
+ continue;
+ }
+ for (IHostFinder hf : hostFinder) {
+ hf.find(decodeIPFromId(arphost.getHostId()));
+ }
+ arphost.sent_count++;
+ logger.debug("ARP Sent from ARPPending List, IP: {}", decodeIPFromId(arphost.getHostId()));
+ } else if (arphost.getSent_count() >= hostRetryCount) {
+ /*
+ * ARP requests have been sent without receiving a
+ * reply, remove this from the pending list
+ */
+ ARPPendingList.remove(entry.getKey());
+ logger.debug(
+ "ARP reply not received after multiple attempts, removing from Pending List IP: {}",
+ decodeIPFromId(arphost.getHostId()));
+ /*
+ * Add this host to a different list which will be
+ * processed on link up events
+ */
+ logger.debug("Adding the host to FailedARPReqList IP: {}", decodeIPFromId(arphost.getHostId()));
+ failedARPReqList.put(entry.getKey(), arphost);
- } else {
- logger.error("Inavlid arp_sent count for entry: {}", entry);
+ } else {
+ logger.error("Inavlid arp_sent count for entry: {}", entry);
+ }
}
+ } catch (IllegalStateException e) {
+ logger.debug("IllegalStateException Received by OutStandingARPHandler from: {}", e.getMessage());
}
}
}
private class ARPRefreshHandler extends TimerTask {
@Override
public void run() {
- if (stopping) {
+ if ((clusterContainerService != null) && !clusterContainerService.amICoordinator()) {
return;
}
- if ((clusterContainerService != null) && !clusterContainerService.amICoordinator()) {
+ if (stopping) {
return;
}
if (!hostRefresh) {
logger.error("ARPRefreshHandler(): hostsDB is not allocated yet:");
return;
}
- for (Entry<IHostId, HostNodeConnector> entry : hostsDB.entrySet()) {
- HostNodeConnector host = entry.getValue();
- if (host.isStaticHost()) {
- /* this host was learned via API3, don't age it out */
- continue;
- }
-
- short arp_cntdown = host.getArpSendCountDown();
- arp_cntdown--;
- if (arp_cntdown > hostRetryCount) {
- host.setArpSendCountDown(arp_cntdown);
- } else if (arp_cntdown <= 0) {
- /*
- * No ARP Reply received in last 2 minutes, remove this host
- * and inform applications
- */
- removeKnownHost(entry.getKey());
- notifyHostLearnedOrRemoved(host, false);
- } else if (arp_cntdown <= hostRetryCount) {
- /*
- * Use the services of arphandler to check if host is still
- * there
- */
- if (logger.isTraceEnabled()) {
- logger.trace(
- "ARP Probing ({}) for {}({})",
- new Object[] { arp_cntdown, host.getNetworkAddress().getHostAddress(),
- HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
+ try {
+ for (Entry<IHostId, HostNodeConnector> entry : hostsDB.entrySet()) {
+ HostNodeConnector host = entry.getValue();
+ if (host.isStaticHost()) {
+ /* this host was learned via API3, don't age it out */
+ continue;
}
- host.setArpSendCountDown(arp_cntdown);
- if (hostFinder == null) {
+
+ short arp_cntdown = host.getArpSendCountDown();
+ arp_cntdown--;
+ if (arp_cntdown > hostRetryCount) {
+ host.setArpSendCountDown(arp_cntdown);
+ } else if (arp_cntdown <= 0) {
/*
- * If hostfinder is not available, then can't send the
- * probe. However, continue the age out the hosts since
- * we don't know if the host is indeed out there or not.
+ * No ARP Reply received in last 2 minutes, remove this
+ * host and inform applications
*/
- logger.trace("ARPHandler is not avaialable, can't send the probe");
- continue;
- }
- for (IHostFinder hf : hostFinder) {
- hf.probe(host);
+ removeKnownHost(entry.getKey());
+ notifyHostLearnedOrRemoved(host, false);
+ } else if (arp_cntdown <= hostRetryCount) {
+ /*
+ * Use the services of arphandler to check if host is
+ * still there
+ */
+ if (logger.isTraceEnabled()) {
+ logger.trace(
+ "ARP Probing ({}) for {}({})",
+ new Object[] { arp_cntdown, host.getNetworkAddress().getHostAddress(),
+ HexEncode.bytesToHexString(host.getDataLayerAddressBytes()) });
+ }
+ host.setArpSendCountDown(arp_cntdown);
+ if (hostFinder == null) {
+ /*
+ * If hostfinder is not available, then can't send
+ * the probe. However, continue the age out the
+ * hosts since we don't know if the host is indeed
+ * out there or not.
+ */
+ logger.trace("ARPHandler is not avaialable, can't send the probe");
+ continue;
+ }
+ for (IHostFinder hf : hostFinder) {
+ hf.probe(host);
+ }
}
}
+ } catch (IllegalStateException e) {
+ logger.debug("IllegalStateException Received by ARPRefreshHandler from: {}", e.getMessage());
}
}
}
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
/**
@Override
public java.lang.AutoCloseable createInstance() {
-
+
RuntimeGeneratedMappingServiceProxy potential = tryToReuseGlobalInstance();
if(potential != null) {
return potential;
BindingIndependentMappingService, //
Delegator<BindingIndependentMappingService>, //
AutoCloseable {
-
+
private BindingIndependentMappingService delegate;
private ServiceReference<BindingIndependentMappingService> reference;
private BundleContext bundleContext;
public DataContainer dataObjectFromDataDom(Class<? extends DataContainer> inputClass, CompositeNode domInput) {
return delegate.dataObjectFromDataDom(inputClass, domInput);
}
-
+
+ @Override
+ public Optional<Class<? extends RpcService>> getRpcServiceClassFor(String namespace, String revision) {
+ return delegate.getRpcServiceClassFor(namespace, revision);
+ }
+
@Override
public void close() throws Exception {
if(delegate != null) {
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http</artifactId>
+ <version>4.0.10.Final</version>
+ </dependency>
<!-- Testing Dependencies -->
<dependency>
<version>1.0.9</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-parser-impl</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
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.controller.sal.restconf.impl.StructuredData;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@GET
@Path("/modules")
- @Produces({Draft02.MediaTypes.API+JSON,Draft02.MediaTypes.API+XML})
+ @Produces({Draft02.MediaTypes.API+XML, Draft02.MediaTypes.API+JSON,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public StructuredData getModules();
+ @GET
+ @Path("/modules/{identifier:.+}")
+ @Produces({Draft02.MediaTypes.API+XML, Draft02.MediaTypes.API+JSON,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+ public StructuredData getModules(@PathParam("identifier") String identifier);
+
+ @GET
+ @Path("/modules/module/{identifier:.+}")
+ @Produces({Draft02.MediaTypes.API+XML, Draft02.MediaTypes.API+JSON,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+ public StructuredData getModule(@PathParam("identifier") String identifier);
+
+ @GET
+ @Path("/operations")
+ @Produces({Draft02.MediaTypes.API+XML, Draft02.MediaTypes.API+JSON,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+ public StructuredData getOperations();
+
+ @GET
+ @Path("/operations/{identifier:.+}")
+ @Produces({Draft02.MediaTypes.API+XML, Draft02.MediaTypes.API+JSON,
+ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+ public StructuredData getOperations(@PathParam("identifier") String identifier);
+
@POST
- @Path("/operations/{identifier}")
+ @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})
public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
@POST
- @Path("/operations/{identifier}")
+ @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})
@Path("/config/{identifier:.+}")
public Response deleteConfigurationData(@PathParam("identifier") String identifier);
+ @GET
+ @Path("/streams/stream/{identifier:.+}")
+ public Response subscribeToStream(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+
}
+ baseType.getClass().getSimpleName() + ".");
}
- // TODO check InstanceIdentifierTypeDefinition
if (baseType instanceof IdentityrefTypeDefinition) {
if (node.getValue() instanceof QName) {
IdentityValuesDTO valueDTO = (IdentityValuesDTO) RestCodec.from(baseType, mountPoint).serialize(
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.osgi.framework.BundleActivator;
private ServiceTracker<Broker, Broker> brokerServiceTrancker;
private BundleContext bundleContext;
private ProviderSession session;
+ private Thread webSocketServerThread;
@Override
public void onSessionInitiated(ProviderSession session) {
bundleContext = context;
brokerServiceTrancker = new ServiceTracker<>(context, Broker.class, this);
brokerServiceTrancker.open();
+ webSocketServerThread = new Thread(new WebSocketServer());
+ webSocketServerThread.setName("Web socket server");
+ webSocketServerThread.start();
}
@Override
e.printStackTrace();
}
}
+ webSocketServerThread.interrupt();
session.close();
brokerServiceTrancker.close();
}
import com.google.gson.stream.JsonWriter;
@Provider
-@Produces({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON,
- MediaType.APPLICATION_JSON })
+@Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON,
+ Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
public enum StructuredDataToJsonProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
import org.w3c.dom.Document;
@Provider
-@Produces({ Draft02.MediaTypes.DATA + RestconfService.XML, Draft02.MediaTypes.OPERATION + RestconfService.XML,
- MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+@Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
+ Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
public enum StructuredDataToXmlProvider implements MessageBodyWriter<StructuredData> {
INSTANCE;
import org.opendaylight.controller.md.sal.common.api.data.DataReader
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession
import org.opendaylight.controller.sal.core.api.data.DataBrokerService
+import org.opendaylight.controller.sal.core.api.mount.MountInstance
import org.opendaylight.controller.sal.rest.impl.RestconfProvider
+import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.RpcResult
import org.opendaylight.yangtools.yang.data.api.CompositeNode
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.slf4j.LoggerFactory
-import org.opendaylight.controller.sal.core.api.mount.MountInstance
class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
return transaction.commit
}
+ def registerToListenDataChanges(ListenerAdapter listener) {
+ checkPreconditions
+ if (listener.listening) {
+ return;
+ }
+ val registration = dataService.registerDataChangeListener(listener.path, listener)
+ listener.setRegistration(registration)
+ }
+
}
package org.opendaylight.controller.sal.restconf.impl
import com.google.common.base.Preconditions
+import com.google.common.base.Splitter
import com.google.common.collect.BiMap
import com.google.common.collect.FluentIterable
import com.google.common.collect.HashBiMap
+import com.google.common.collect.Lists
import java.net.URI
import java.net.URLDecoder
import java.net.URLEncoder
onGlobalContextUpdated(schemas)
}
- public def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
+ def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
+ return restconfInstance.toIdentifier(false)
+ }
+
+ def InstanceIdWithSchemaNode toMountPointIdentifier(String restconfInstance) {
+ return restconfInstance.toIdentifier(true)
+ }
+
+ private def InstanceIdWithSchemaNode toIdentifier(String restconfInstance, boolean toMountPointIdentifier) {
checkPreconditions
- val pathArgs = restconfInstance.split("/");
+ val pathArgs = Lists.newArrayList(Splitter.on("/").split(restconfInstance))
+ pathArgs.omitFirstAndLastEmptyString
if (pathArgs.empty) {
return null;
}
- if (pathArgs.head.empty) {
- pathArgs.remove(0)
- }
val startModule = pathArgs.head.toModuleName();
if (startModule === null) {
throw new ResponseException(BAD_REQUEST, "First node in URI has to be in format \"moduleName:nodeName\"")
}
- val iiWithSchemaNode = collectPathArguments(InstanceIdentifier.builder(), pathArgs,
- globalSchema.getLatestModule(startModule), null);
+ var InstanceIdWithSchemaNode iiWithSchemaNode = null;
+ if (toMountPointIdentifier) {
+ iiWithSchemaNode = collectPathArguments(InstanceIdentifier.builder(), pathArgs,
+ globalSchema.getLatestModule(startModule), null, true);
+ } else {
+ iiWithSchemaNode = collectPathArguments(InstanceIdentifier.builder(), pathArgs,
+ globalSchema.getLatestModule(startModule), null, false);
+ }
if (iiWithSchemaNode === null) {
throw new ResponseException(BAD_REQUEST, "URI has bad format")
}
return iiWithSchemaNode
}
+ private def omitFirstAndLastEmptyString(List<String> list) {
+ if (list.empty) {
+ return list;
+ }
+ if (list.head.empty) {
+ list.remove(0)
+ }
+ if (list.empty) {
+ return list;
+ }
+ if (list.last.empty) {
+ list.remove(list.indexOf(list.last))
+ }
+ return list;
+ }
+
private def getLatestModule(SchemaContext schema, String moduleName) {
checkArgument(schema !== null);
checkArgument(moduleName !== null && !moduleName.empty)
return moduleSchemas?.filterLatestModule
}
+ def findModuleByNameAndRevision(QName module) {
+ checkPreconditions
+ checkArgument(module !== null && module.localName !== null && module.revision !== null)
+ return globalSchema.findModuleByName(module.localName, module.revision)
+ }
+
+ def findModuleByNameAndRevision(MountInstance mountPoint, QName module) {
+ checkPreconditions
+ checkArgument(module !== null && module.localName !== null && module.revision !== null && mountPoint !== null)
+ return mountPoint.schemaContext?.findModuleByName(module.localName, module.revision)
+ }
+
+ def getDataNodeContainerFor(InstanceIdentifier path) {
+ checkPreconditions
+ val elements = path.path;
+ val startQName = elements.head.nodeType;
+ val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
+ var node = initialModule as DataNodeContainer;
+ for (element : elements) {
+ val potentialNode = node.childByQName(element.nodeType);
+ if (potentialNode === null || !potentialNode.listOrContainer) {
+ return null
+ }
+ node = potentialNode as DataNodeContainer
+ }
+ return node
+ }
+
def String toFullRestconfIdentifier(InstanceIdentifier path) {
checkPreconditions
val elements = path.path;
val ret = new StringBuilder();
- val startQName = elements.get(0).nodeType;
+ val startQName = elements.head.nodeType;
val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
- var node = initialModule as DataSchemaNode;
+ var node = initialModule as DataNodeContainer;
for (element : elements) {
- node = node.childByQName(element.nodeType);
- ret.append(element.toRestconfIdentifier(node));
+ val potentialNode = node.childByQName(element.nodeType);
+ if (!potentialNode.listOrContainer) {
+ return null
+ }
+ node = potentialNode as DataNodeContainer
+ ret.append(element.convertToRestconfIdentifier(node));
}
return ret.toString
}
- private def dispatch CharSequence toRestconfIdentifier(NodeIdentifier argument, DataSchemaNode node) {
+ private def dispatch CharSequence convertToRestconfIdentifier(NodeIdentifier argument, ContainerSchemaNode node) {
'''/«argument.nodeType.toRestconfIdentifier()»'''
}
- private def dispatch CharSequence toRestconfIdentifier(NodeIdentifierWithPredicates argument, ListSchemaNode node) {
+ private def dispatch CharSequence convertToRestconfIdentifier(NodeIdentifierWithPredicates argument, ListSchemaNode node) {
val nodeIdentifier = argument.nodeType.toRestconfIdentifier();
val keyValues = argument.keyValues;
return '''/«nodeIdentifier»/«FOR key : node.keyDefinition SEPARATOR "/"»«keyValues.get(key).toUriString»«ENDFOR»'''
}
- private def dispatch CharSequence toRestconfIdentifier(PathArgument argument, DataSchemaNode node) {
+ private def dispatch CharSequence convertToRestconfIdentifier(PathArgument argument, DataNodeContainer node) {
throw new IllegalArgumentException("Conversion of generic path argument is not supported");
}
return module?.namespace
}
+ def getAllModules(MountInstance mountPoint) {
+ checkPreconditions
+ return mountPoint?.schemaContext?.modules
+ }
+
+ def getAllModules() {
+ checkPreconditions
+ return globalSchema.modules
+ }
+
def CharSequence toRestconfIdentifier(QName qname) {
checkPreconditions
var module = uriToModuleName.get(qname.namespace)
return container.dataNodeChildByQName(name);
}
+ private static dispatch def DataSchemaNode childByQName(Module container, QName name) {
+ return container.dataNodeChildByQName(name);
+ }
+
private static dispatch def DataSchemaNode childByQName(DataSchemaNode container, QName name) {
return null;
}
}
private def InstanceIdWithSchemaNode collectPathArguments(InstanceIdentifierBuilder builder, List<String> strings,
- DataNodeContainer parentNode, MountInstance mountPoint) {
+ DataNodeContainer parentNode, MountInstance mountPoint, boolean returnJustMountPoint) {
checkNotNull(strings)
if (parentNode === null) {
return null;
throw new ResponseException(BAD_REQUEST, "Mount point does not contain any schema with modules.")
}
+ if (returnJustMountPoint) {
+ return new InstanceIdWithSchemaNode(InstanceIdentifier.builder().toInstance, mountPointSchema, mount)
+ }
+
if (strings.size == 1) { // any data node is not behind mount point
return new InstanceIdWithSchemaNode(InstanceIdentifier.builder().toInstance, mountPointSchema, mount)
}
}
return collectPathArguments(InstanceIdentifier.builder(), strings.subList(1, strings.size),
- moduleBehindMountPoint, mount);
+ moduleBehindMountPoint, mount, returnJustMountPoint);
}
var Module module = null;
}
}
- if (!(targetNode instanceof ListSchemaNode) && !(targetNode instanceof ContainerSchemaNode)) {
+ if (!targetNode.isListOrContainer) {
throw new ResponseException(BAD_REQUEST,"URI has bad format. Node \"" + strings.head + "\" must be Container or List yang type.")
}
// Number of consumed elements
}
if (targetNode instanceof DataNodeContainer) {
val remaining = strings.subList(consumed, strings.length);
- val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer, mountPoint);
+ val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer, mountPoint, returnJustMountPoint);
return result
}
private def QName toQName(String name) {
val module = name.toModuleName;
val node = name.toNodeName;
- val namespace = FluentIterable.from(globalSchema.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
+ val namespace = FluentIterable.from(globalSchema.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)])
.transform[QName.create(namespace,revision,it.name)].findFirst[module == localName]
- ;
- return QName.create(namespace,node);
+ if (namespace === null) {
+ return null
+ }
+ return QName.create(namespace, node);
+ }
+
+ private def boolean isListOrContainer(DataSchemaNode node) {
+ return ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode))
}
def getRpcDefinition(String name) {
- return qnameToRpc.get(name.toQName)
+ val validName = name.toQName
+ if (validName === null) {
+ return null
+ }
+ return qnameToRpc.get(validName)
}
override onGlobalContextUpdated(SchemaContext context) {
package org.opendaylight.controller.sal.restconf.impl
import com.google.common.base.Preconditions
+import com.google.common.base.Splitter
+import com.google.common.collect.Lists
import java.net.URI
+import java.text.ParseException
+import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.HashMap
import java.util.List
+import java.util.Set
import javax.ws.rs.core.Response
+import javax.ws.rs.core.UriInfo
import org.opendaylight.controller.md.sal.common.api.TransactionStatus
import org.opendaylight.controller.sal.core.api.mount.MountInstance
import org.opendaylight.controller.sal.rest.api.RestconfService
+import org.opendaylight.controller.sal.streams.listeners.Notificator
+import org.opendaylight.controller.sal.streams.websockets.WebSocketServer
import org.opendaylight.yangtools.yang.common.QName
import org.opendaylight.yangtools.yang.common.RpcResult
import org.opendaylight.yangtools.yang.data.api.CompositeNode
import org.opendaylight.yangtools.yang.model.api.SchemaContext
import org.opendaylight.yangtools.yang.model.api.TypeDefinition
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
+import org.opendaylight.yangtools.yang.model.util.EmptyType
+import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
+import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
import static javax.ws.rs.core.Response.Status.*
val static RestconfImpl INSTANCE = new RestconfImpl
val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
+ val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
+ val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
+ val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
+ val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
+ val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
+ val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
+ val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
+ val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
+ val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
+ val static SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
+ val static SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription"
@Property
BrokerFacade broker
}
override getModules() {
- throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ val restconfModule = getRestconfModule()
+ val List<Node<?>> modulesAsData = new ArrayList
+ val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
+ for (module : allModules) {
+ modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
+ }
+ val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
+ val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
+ return new StructuredData(modulesNode, modulesSchemaNode, null)
+ }
+
+ override getModules(String identifier) {
+ var Set<Module> modules = null
+ var MountInstance mountPoint = null
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ mountPoint = identifier.toMountPointIdentifier.mountPoint
+ modules = mountPoint.allModules
+ } else {
+ throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
+ }
+ val List<Node<?>> modulesAsData = new ArrayList
+ val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
+ for (module : modules) {
+ modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
+ }
+ val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
+ val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
+ return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
+ }
+
+ override getModule(String identifier) {
+ val moduleNameAndRevision = identifier.moduleNameAndRevision
+ var Module module = null
+ var MountInstance mountPoint = null
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ mountPoint = identifier.toMountPointIdentifier.mountPoint
+ module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
+ } else {
+ module = findModuleByNameAndRevision(moduleNameAndRevision)
+ }
+ if (module === null) {
+ throw new ResponseException(BAD_REQUEST,
+ "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
+ moduleNameAndRevision.revision + "' was not found.")
+ }
+ val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
+ val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
+ return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
+ }
+
+ override getOperations() {
+ return operationsFromModulesToStructuredData(allModules,null)
+ }
+
+ override getOperations(String identifier) {
+ var Set<Module> modules = null
+ var MountInstance mountPoint = null
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ mountPoint = identifier.toMountPointIdentifier.mountPoint
+ modules = mountPoint.allModules
+ } else {
+ throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
+ }
+ return operationsFromModulesToStructuredData(modules,mountPoint)
+ }
+
+ private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
+ val List<Node<?>> operationsAsData = new ArrayList
+ val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)
+ val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
+ for (module : modules) {
+ for (rpc : module.rpcs) {
+ operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
+ val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
+ fakeRpcSchemaNode.setAugmenting(true)
+ fakeRpcSchemaNode.setType(EmptyType.instance)
+ fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)
+ }
+ }
+ val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
+ return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)
+ }
+
+ private def Module getRestconfModule() {
+ val restconfModule = findModuleByNameAndRevision(
+ QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
+ RESTCONF_MODULE_DRAFT02_NAME))
+ if (restconfModule === null) {
+ throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
+ }
+ return restconfModule
+ }
+
+ private def QName getModuleNameAndRevision(String identifier) {
+ val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
+ var moduleNameAndRevision = "";
+ if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
+ moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
+ } else (
+ moduleNameAndRevision = identifier
+ )
+ val pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
+ if (pathArgs.length < 2) {
+ throw new ResponseException(BAD_REQUEST,
+ "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
+ }
+ try {
+ val moduleName = pathArgs.head
+ val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
+ return QName.create(null, moduleRevision, moduleName)
+ } catch(ParseException e) {
+ throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
+ }
+ }
+
+ private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
+ val List<Node<?>> moduleNodeValues = new ArrayList
+ val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
+ moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
+ val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
+ moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
+ val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
+ moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
+ val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
+ for (feature : module.features) {
+ moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
+ }
+ return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
+ }
+
+ private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
+ val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
+ val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
+ if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
+ return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
+ } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
+ return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
+ } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
+ val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
+ return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
+ }
+ return null
}
override getRoot() {
}
override invokeRpc(String identifier, CompositeNode payload) {
+ val rpc = identifier.rpcDefinition
+ if (rpc === null) {
+ throw new ResponseException(NOT_FOUND, "RPC does not exist.");
+ }
+ if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
+ val value = normalizeNode(payload, rpc.input, null)
+ val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
+ val pathValue = pathNode?.value
+ if (pathValue === null && !(pathValue instanceof InstanceIdentifier)) {
+ throw new ResponseException(INTERNAL_SERVER_ERROR, "Instance identifier was not normalized correctly.");
+ }
+ val pathIdentifier = (pathValue as InstanceIdentifier)
+ var String streamName = null
+ if (!pathIdentifier.path.nullOrEmpty) {
+ streamName = Notificator.createStreamNameFromUri(pathIdentifier.toFullRestconfIdentifier)
+ }
+ if (streamName.nullOrEmpty) {
+ throw new ResponseException(BAD_REQUEST, "Path is empty or contains data node which is not Container or List build-in type.");
+ }
+ val streamNameNode = NodeFactory.createImmutableSimpleNode(QName.create(rpc.output.QName, "stream-name"), null, streamName)
+ val List<Node<?>> output = new ArrayList
+ output.add(streamNameNode)
+ val responseData = NodeFactory.createMutableCompositeNode(rpc.output.QName, null, output, null, null)
+
+ if (!Notificator.existListenerFor(pathIdentifier)) {
+ Notificator.createListener(pathIdentifier, streamName)
+ }
+
+ return new StructuredData(responseData, rpc.output, null)
+ }
return callRpc(identifier.rpcDefinition, payload)
}
}
}
+ override subscribeToStream(String identifier, UriInfo uriInfo) {
+ val streamName = Notificator.createStreamNameFromUri(identifier)
+ if (streamName.nullOrEmpty) {
+ throw new ResponseException(BAD_REQUEST, "Stream name is empty.")
+ }
+ val listener = Notificator.getListenerFor(streamName);
+ if (listener === null) {
+ throw new ResponseException(BAD_REQUEST, "Stream was not found.")
+ }
+ broker.registerToListenDataChanges(listener)
+ val uriBuilder = uriInfo.getAbsolutePathBuilder()
+ val uriToWebsocketServer = uriBuilder.port(WebSocketServer.PORT).replacePath(streamName).build()
+ return Response.status(OK).location(uriToWebsocketServer).build
+ }
+
private def dispatch URI namespace(CompositeNode data) {
return data.nodeType.namespace
}
--- /dev/null
+package org.opendaylight.controller.sal.streams.listeners;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.netty.util.internal.ConcurrentSet;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Executors;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+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.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.rest.impl.XmlMapper;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.google.common.base.Preconditions;
+import com.google.common.eventbus.AsyncEventBus;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+
+public class ListenerAdapter implements DataChangeListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(ListenerAdapter.class);
+ private final XmlMapper xmlMapper = new XmlMapper();
+ private final SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ");
+
+ private final InstanceIdentifier path;
+ private ListenerRegistration<DataChangeListener> registration;
+ private final String streamName;
+ private Set<Channel> subscribers = new ConcurrentSet<>();
+ private final EventBus eventBus;
+ private final EventBusChangeRecorder eventBusChangeRecorder;
+
+ ListenerAdapter(InstanceIdentifier path, String streamName) {
+ Preconditions.checkNotNull(path);
+ Preconditions.checkArgument(streamName != null && !streamName.isEmpty());
+ this.path = path;
+ this.streamName = streamName;
+ eventBus = new AsyncEventBus(Executors.newSingleThreadExecutor());
+ eventBusChangeRecorder = new EventBusChangeRecorder();
+ eventBus.register(eventBusChangeRecorder);
+ }
+
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ if (!change.getCreatedConfigurationData().isEmpty() || !change.getCreatedOperationalData().isEmpty()
+ || !change.getUpdatedConfigurationData().isEmpty() || !change.getUpdatedOperationalData().isEmpty()
+ || !change.getRemovedConfigurationData().isEmpty() || !change.getRemovedOperationalData().isEmpty()) {
+ String xml = prepareXmlFrom(change);
+ Event event = new Event(EventType.NOTIFY);
+ event.setData(xml);
+ eventBus.post(event);
+ }
+ }
+
+ private final class EventBusChangeRecorder {
+ @Subscribe public void recordCustomerChange(Event event) {
+ if (event.getType() == EventType.REGISTER) {
+ Channel subscriber = event.getSubscriber();
+ if (!subscribers.contains(subscriber)) {
+ subscribers.add(subscriber);
+ }
+ } else if (event.getType() == EventType.DEREGISTER) {
+ subscribers.remove(event.getSubscriber());
+ Notificator.removeListenerIfNoSubscriberExists(ListenerAdapter.this);
+ } else if (event.getType() == EventType.NOTIFY) {
+ for (Channel subscriber : subscribers) {
+ if (subscriber.isActive()) {
+ logger.debug("Data are sent to subscriber {}:", subscriber.remoteAddress());
+ subscriber.writeAndFlush(new TextWebSocketFrame(event.getData()));
+ } else {
+ logger.debug("Subscriber {} is removed - channel is not active yet.", subscriber.remoteAddress());
+ subscribers.remove(subscriber);
+ }
+ }
+ }
+ }
+ }
+
+ private final class Event {
+ private final EventType type;
+ private Channel subscriber;
+ private String data;
+
+ public Event(EventType type) {
+ this.type = type;
+ }
+
+ public Channel getSubscriber() {
+ return subscriber;
+ }
+
+ public void setSubscriber(Channel subscriber) {
+ this.subscriber = subscriber;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public EventType getType() {
+ return type;
+ }
+ }
+
+ private enum EventType {
+ REGISTER,
+ DEREGISTER,
+ NOTIFY;
+ }
+
+ private String prepareXmlFrom(DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ Document doc = createDocument();
+ Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
+ "notification");
+ doc.appendChild(notificationElement);
+
+ Element eventTimeElement = doc.createElement("eventTime");
+ eventTimeElement.setTextContent(toRFC3339(new Date()));
+ notificationElement.appendChild(eventTimeElement);
+
+ Element dataChangedNotificationEventElement = doc.createElementNS(
+ "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
+ addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change);
+ notificationElement.appendChild(dataChangedNotificationEventElement);
+
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TransformerFactory tf = TransformerFactory.newInstance();
+ 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(new OutputStreamWriter(out, "UTF-8")));
+ byte[] charData = out.toByteArray();
+ return new String(charData, "UTF-8");
+ } catch (TransformerException | UnsupportedEncodingException e) {
+ String msg = "Error during transformation of Document into String";
+ logger.error(msg, e);
+ return msg;
+ }
+ }
+
+ private String toRFC3339(Date d) {
+ return rfc3339.format(d).replaceAll("(\\d\\d)(\\d\\d)$", "$1:$2");
+ }
+
+ private Document createDocument() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ Document doc = null;
+ try {
+ DocumentBuilder bob = dbf.newDocumentBuilder();
+ doc = bob.newDocument();
+ } catch (ParserConfigurationException e) {
+ return null;
+ }
+ return doc;
+ }
+
+ private void addValuesToDataChangedNotificationEventElement(Document doc,
+ Element dataChangedNotificationEventElement, DataChangeEvent<InstanceIdentifier, CompositeNode> change) {
+ addValuesFromDataToElement(doc, change.getCreatedConfigurationData(), dataChangedNotificationEventElement, Store.CONFIG, Operation.CREATED);
+ addValuesFromDataToElement(doc, change.getCreatedOperationalData(), dataChangedNotificationEventElement, Store.OPERATION, Operation.CREATED);
+ if (change.getCreatedConfigurationData().isEmpty()) {
+ addValuesFromDataToElement(doc, change.getUpdatedConfigurationData(), dataChangedNotificationEventElement, Store.CONFIG, Operation.UPDATED);
+ }
+ if (change.getCreatedOperationalData().isEmpty()) {
+ addValuesFromDataToElement(doc, change.getUpdatedOperationalData(), dataChangedNotificationEventElement, Store.OPERATION, Operation.UPDATED);
+ }
+ addValuesFromDataToElement(doc, change.getRemovedConfigurationData(), dataChangedNotificationEventElement, Store.CONFIG, Operation.DELETED);
+ addValuesFromDataToElement(doc, change.getRemovedOperationalData(), dataChangedNotificationEventElement, Store.OPERATION, Operation.DELETED);
+ }
+
+ private void addValuesFromDataToElement(Document doc, Set<InstanceIdentifier> data, Element element, Store store,
+ Operation operation) {
+ if (data == null || data.isEmpty()) {
+ return;
+ }
+ for (InstanceIdentifier path : data) {
+ Node node = createDataChangeEventElement(doc, path, null, store, operation);
+ element.appendChild(node);
+ }
+ }
+
+ private void addValuesFromDataToElement(Document doc, Map<InstanceIdentifier, CompositeNode> data, Element element, Store store,
+ Operation operation) {
+ if (data == null || data.isEmpty()) {
+ return;
+ }
+ for (Entry<InstanceIdentifier, CompositeNode> entry : data.entrySet()) {
+ Node node = createDataChangeEventElement(doc, entry.getKey(), entry.getValue(), store, operation);
+ element.appendChild(node);
+ }
+ }
+
+ private Node createDataChangeEventElement(Document doc, InstanceIdentifier path, CompositeNode data, Store store,
+ Operation operation) {
+ Element dataChangeEventElement = doc.createElement("data-change-event");
+
+ Element pathElement = doc.createElement("path");
+ addPathAsValueToElement(path, pathElement);
+ dataChangeEventElement.appendChild(pathElement);
+
+ Element storeElement = doc.createElement("store");
+ storeElement.setTextContent(store.value);
+ dataChangeEventElement.appendChild(storeElement);
+
+ Element operationElement = doc.createElement("operation");
+ operationElement.setTextContent(operation.value);
+ dataChangeEventElement.appendChild(operationElement);
+
+ if (data != null) {
+ Element dataElement = doc.createElement("data");
+ Node dataAnyXml = translateToXml(path, data);
+ Node adoptedNode = doc.adoptNode(dataAnyXml);
+ dataElement.appendChild(adoptedNode);
+ dataChangeEventElement.appendChild(dataElement);
+ }
+
+ return dataChangeEventElement;
+ }
+
+ private Node translateToXml(InstanceIdentifier path, CompositeNode data) {
+ DataNodeContainer schemaNode = ControllerContext.getInstance().getDataNodeContainerFor(path);
+ if (schemaNode == null) {
+ logger.info("Path '{}' contains node with unsupported type (supported type is Container or List) or some node was not found.", path);
+ return null;
+ }
+ try {
+ Document xml = xmlMapper.write(data, schemaNode);
+ return xml.getFirstChild();
+ } catch (UnsupportedDataTypeException e) {
+ logger.error("Error occured during translation of notification to XML.", e);
+ return null;
+ }
+ }
+
+ private void addPathAsValueToElement(InstanceIdentifier path, Element element) {
+ // Map< key = namespace, value = prefix>
+ Map<String, String> prefixes = new HashMap<>();
+ InstanceIdentifier instanceIdentifier = path;
+ StringBuilder textContent = new StringBuilder();
+ for (PathArgument pathArgument : instanceIdentifier.getPath()) {
+ textContent.append("/");
+ writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
+ if (pathArgument instanceof NodeIdentifierWithPredicates) {
+ Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+ for (QName keyValue : predicates.keySet()) {
+ String predicateValue = String.valueOf(predicates.get(keyValue));
+ textContent.append("[");
+ writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
+ 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());
+ }
+
+ private static void writeIdentifierWithNamespacePrefix(Element element, StringBuilder textContent, QName qName,
+ Map<String, String> prefixes) {
+ String namespace = qName.getNamespace().toString();
+ String prefix = prefixes.get(namespace);
+ if (prefix == null) {
+ prefix = qName.getPrefix();
+ if (prefix == null || prefix.isEmpty() || prefixes.containsValue(prefix)) {
+ prefix = generateNewPrefix(prefixes.values());
+ }
+ }
+
+ element.setAttribute("xmlns:" + prefix, namespace.toString());
+ textContent.append(prefix);
+ prefixes.put(namespace, prefix);
+
+ textContent.append(":");
+ textContent.append(qName.getLocalName());
+ }
+
+ private static String generateNewPrefix(Collection<String> prefixes) {
+ StringBuilder result = null;
+ Random random = new Random();
+ do {
+ result = new StringBuilder();
+ for (int i = 0; i < 4; i++) {
+ int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26);
+ result.append(Character.toChars(randomNumber));
+ }
+ } while (prefixes.contains(result.toString()));
+
+ return result.toString();
+ }
+
+ public InstanceIdentifier getPath() {
+ return path;
+ }
+
+ public void setRegistration(ListenerRegistration<DataChangeListener> registration) {
+ this.registration = registration;
+ }
+
+ public String getStreamName() {
+ return streamName;
+ }
+
+ public void close() throws Exception {
+ subscribers = new ConcurrentSet<>();
+ registration.close();
+ registration = null;
+ eventBus.unregister(eventBusChangeRecorder);
+ }
+
+ public boolean isListening() {
+ return registration == null ? false : true;
+ }
+
+ public void addSubscriber(Channel subscriber) {
+ if (!subscriber.isActive()) {
+ logger.debug("Channel is not active between websocket server and subscriber {}"
+ + subscriber.remoteAddress());
+ }
+ Event event = new Event(EventType.REGISTER);
+ event.setSubscriber(subscriber);
+ eventBus.post(event);
+ }
+
+ public void removeSubscriber(Channel subscriber) {
+ logger.debug("Subscriber {} is removed.", subscriber.remoteAddress());
+ Event event = new Event(EventType.DEREGISTER);
+ event.setSubscriber(subscriber);
+ eventBus.post(event);
+ }
+
+ public boolean hasSubscribers() {
+ return !subscribers.isEmpty();
+ }
+
+ private static enum Store {
+ CONFIG("config"),
+ OPERATION("operation");
+
+ private final String value;
+
+ private Store(String value) {
+ this.value = value;
+ }
+ }
+
+ private static enum Operation {
+ CREATED("created"),
+ UPDATED("updated"),
+ DELETED("deleted");
+
+ private final String value;
+
+ private Operation(String value) {
+ this.value = value;
+ }
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.streams.listeners;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class Notificator {
+
+ private static Map<String, ListenerAdapter> listenersByStreamName = new ConcurrentHashMap<>();
+ private static Map<InstanceIdentifier, ListenerAdapter> listenersByInstanceIdentifier = new ConcurrentHashMap<>();
+ private static final Lock lock = new ReentrantLock();
+
+ private Notificator() {
+ }
+
+ public static ListenerAdapter getListenerFor(String streamName) {
+ return listenersByStreamName.get(streamName);
+ }
+
+ public static ListenerAdapter getListenerFor(InstanceIdentifier path) {
+ return listenersByInstanceIdentifier.get(path);
+ }
+
+ public static boolean existListenerFor(InstanceIdentifier path) {
+ return listenersByInstanceIdentifier.containsKey(path);
+ }
+
+ public static ListenerAdapter createListener(InstanceIdentifier path, String streamName) {
+ ListenerAdapter listener = new ListenerAdapter(path, streamName);
+ lock.lock();
+ listenersByInstanceIdentifier.put(path, listener);
+ listenersByStreamName.put(streamName, listener);
+ lock.unlock();
+ return listener;
+ }
+
+ public static void removeListener(InstanceIdentifier path) {
+ ListenerAdapter listener = listenersByInstanceIdentifier.get(path);
+ deleteListener(listener);
+ }
+
+ public static String createStreamNameFromUri(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());
+ }
+ return result;
+ }
+
+ public static void removeAllListeners() {
+ for (ListenerAdapter listener : listenersByInstanceIdentifier.values()) {
+ try {
+ listener.close();
+ } catch (Exception e) {
+ }
+ }
+ lock.lock();
+ listenersByStreamName = new ConcurrentHashMap<>();
+ listenersByInstanceIdentifier = new ConcurrentHashMap<>();
+ lock.unlock();
+ }
+
+ public static void removeListenerIfNoSubscriberExists(ListenerAdapter listener) {
+ if (!listener.hasSubscribers()) {
+ deleteListener(listener);
+ }
+ }
+
+ private static void deleteListener(ListenerAdapter listener) {
+ if (listener != null) {
+ try {
+ listener.close();
+ } catch (Exception e) {
+ }
+ lock.lock();
+ listenersByInstanceIdentifier.remove(listener.getPath());
+ listenersByStreamName.remove(listener).getStreamName();
+ lock.unlock();
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.streams.websockets;
+
+import org.opendaylight.controller.sal.streams.listeners.Notificator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+public class WebSocketServer implements Runnable {
+
+ private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
+
+ public static final int PORT = 8181;
+ private EventLoopGroup bossGroup;
+ private EventLoopGroup workerGroup;
+
+ @Override
+ public void run() {
+ bossGroup = new NioEventLoopGroup();
+ workerGroup = new NioEventLoopGroup();
+ try {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(bossGroup, workerGroup)
+ .channel(NioServerSocketChannel.class)
+ .childHandler(new WebSocketServerInitializer());
+
+ Channel ch = b.bind(PORT).sync().channel();
+ logger.info("Web socket server started at port {}.", PORT);
+
+ ch.closeFuture().sync();
+ } catch (InterruptedException e) {
+ // NOOP
+ } finally {
+ stop();
+ }
+ }
+
+ private void stop() {
+ Notificator.removeAllListeners();
+ if (bossGroup != null) {
+ bossGroup.shutdownGracefully();
+ }
+ if (workerGroup != null) {
+ workerGroup.shutdownGracefully();
+ }
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.sal.streams.websockets;
+
+import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
+import static io.netty.handler.codec.http.HttpHeaders.setContentLength;
+import static io.netty.handler.codec.http.HttpHeaders.Names.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.HttpVersion.HTTP_1_1;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+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.io.IOException;
+
+import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.controller.sal.streams.listeners.Notificator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
+
+ private static final Logger logger = LoggerFactory.getLogger(WebSocketServerHandler.class);
+
+ private WebSocketServerHandshaker handshaker;
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof FullHttpRequest) {
+ handleHttpRequest(ctx, (FullHttpRequest) msg);
+ } else if (msg instanceof WebSocketFrame) {
+ handleWebSocketFrame(ctx, (WebSocketFrame) msg);
+ }
+ }
+
+ private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req)
+ throws Exception {
+ // Handle a bad request.
+ if (!req.getDecoderResult().isSuccess()) {
+ sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
+ return;
+ }
+
+ // Allow only GET methods.
+ if (req.getMethod() != GET) {
+ sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
+ return;
+ }
+
+ String streamName = Notificator.createStreamNameFromUri(req.getUri());
+ ListenerAdapter listener = Notificator.getListenerFor(streamName);
+ if (listener != null) {
+ listener.addSubscriber(ctx.channel());
+ logger.debug("Subscriber successfully registered.");
+ } else {
+ logger.error("Listener for stream with name '{}' was not found.", streamName);
+ sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR));
+ }
+
+ // Handshake
+ WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
+ getWebSocketLocation(req), null, false);
+ handshaker = wsFactory.newHandshaker(req);
+ if (handshaker == null) {
+ WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
+ } else {
+ handshaker.handshake(ctx.channel(), req);
+ }
+
+ }
+
+ private static void sendHttpResponse(ChannelHandlerContext ctx,
+ HttpRequest req, FullHttpResponse res) {
+ // Generate an error page if response getStatus code is not OK (200).
+ if (res.getStatus().code() != 200) {
+ ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
+ res.content().writeBytes(buf);
+ buf.release();
+ setContentLength(res, res.content().readableBytes());
+ }
+
+ // Send the response and close the connection if necessary.
+ ChannelFuture f = ctx.channel().writeAndFlush(res);
+ if (!isKeepAlive(req) || res.getStatus().code() != 200) {
+ f.addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+
+ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws IOException {
+ if (frame instanceof CloseWebSocketFrame) {
+ handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
+ String streamName = Notificator.createStreamNameFromUri(((CloseWebSocketFrame) frame).reasonText());
+ ListenerAdapter listener = Notificator.getListenerFor(streamName);
+ if (listener != null) {
+ listener.removeSubscriber(ctx.channel());
+ logger.debug("Subscriber successfully registered.");
+ }
+ Notificator.removeListenerIfNoSubscriberExists(listener);
+ return;
+ } else if (frame instanceof PingWebSocketFrame) {
+ ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
+ return;
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
+ throws Exception {
+ if (cause instanceof java.nio.channels.ClosedChannelException == false) {
+ //cause.printStackTrace();
+ }
+ ctx.close();
+ }
+
+ private static String getWebSocketLocation(HttpRequest req) {
+ return "http://" + req.headers().get(HOST) + req.getUri();
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.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;
+
+public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
+
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ChannelPipeline pipeline = ch.pipeline();
+ pipeline.addLast("codec-http", new HttpServerCodec());
+ pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
+ pipeline.addLast("handler", new WebSocketServerHandler());
+ }
+
+}
*/
package org.opendaylight.controller.sal.restconf.impl.test;
+import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
import static org.opendaylight.controller.sal.restconf.impl.test.RestOperationUtils.createUri;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.ws.rs.core.Response;
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.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
private static SchemaContext schemaContextTestModule;
private static CompositeNode answerFromGet;
+ private static SchemaContext schemaContextModules;
+ private static SchemaContext schemaContextBehindMountPoint;
+
@BeforeClass
public static void init() throws FileNotFoundException {
schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs");
restconfImpl.setBroker(brokerFacade);
restconfImpl.setControllerContext(controllerContext);
answerFromGet = prepareCompositeNodeWithIetfInterfacesInterfacesData();
+
+ schemaContextModules = TestUtils.loadSchemaContext("/modules");
+ schemaContextBehindMountPoint = TestUtils.loadSchemaContext("/modules/modules-behind-mount-point");
}
@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());
+ // 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(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
ControllerContext.getInstance().setMountService(mockMountService);
-
+
String uri = createUri("/config/",
"ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/cont1");
assertEquals(200, get(uri, MediaType.APPLICATION_XML));
@Test
public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException {
- when(brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
+ when(
+ brokerFacade.readConfigurationDataBehindMountPoint(any(MountInstance.class),
any(InstanceIdentifier.class))).thenReturn(prepareCnDataForMountPointTest());
MountInstance mountInstance = mock(MountInstance.class);
when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
ControllerContext.getInstance().setMountService(mockMountService);
- String uri = createUri("/config/",
- "ietf-interfaces:interfaces/interface/0/yang-ext:mount/");
+ String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount/");
assertEquals(200, get(uri, MediaType.APPLICATION_XML));
}
+ // /modules
+ @Test
+ public void getModulesTest() throws UnsupportedEncodingException, FileNotFoundException {
+ ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
+
+ String uri = createUri("/modules", "");
+
+ Response response = target(uri).request("application/yang.api+json").get();
+ validateModulesResponseJson(response);
+
+ response = target(uri).request("application/yang.api+xml").get();
+ validateModulesResponseXml(response);
+ }
+
+ // /modules/module
+ @Test
+ public void getModuleTest() throws FileNotFoundException, UnsupportedEncodingException {
+ ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
+
+ String uri = createUri("/modules/module/module2/2014-01-02", "");
+
+ Response response = target(uri).request("application/yang.api+xml").get();
+ assertEquals(200, response.getStatus());
+ String responseBody = response.readEntity(String.class);
+ assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
+ .find());
+ String[] split = responseBody.split("<module");
+ assertEquals("<module element is returned more then once",2,split.length);
+
+ response = target(uri).request("application/yang.api+json").get();
+ assertEquals(200, response.getStatus());
+ responseBody = response.readEntity(String.class);
+ assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
+ .find());
+ split = responseBody.split("\"module\"");
+ assertEquals("\"module\" element is returned more then once",2,split.length);
+
+ }
+
+ // /operations
+ @Test
+ public void getOperationsTest() throws FileNotFoundException, UnsupportedEncodingException {
+ ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
+
+ String uri = createUri("/operations", "");
+
+ Response response = target(uri).request("application/yang.api+xml").get();
+ assertEquals(200, response.getStatus());
+ String responseBody = response.readEntity(String.class);
+ assertTrue("Xml response for /operations dummy-rpc1-module1 is incorrect",
+ validateOperationsResponseXml(responseBody, "dummy-rpc1-module1", "module:1").find());
+ assertTrue("Xml response for /operations dummy-rpc2-module1 is incorrect",
+ validateOperationsResponseXml(responseBody, "dummy-rpc2-module1", "module:1").find());
+ assertTrue("Xml response for /operations dummy-rpc1-module2 is incorrect",
+ validateOperationsResponseXml(responseBody, "dummy-rpc1-module2", "module:2").find());
+ assertTrue("Xml response for /operations dummy-rpc2-module2 is incorrect",
+ validateOperationsResponseXml(responseBody, "dummy-rpc2-module2", "module:2").find());
+
+ response = target(uri).request("application/yang.api+json").get();
+ assertEquals(200, response.getStatus());
+ 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());
+
+ }
+
+ // /operations/pathToMountPoint/yang-ext:mount
+ @Test
+ public void getOperationsBehindMountPointTest() throws FileNotFoundException, UnsupportedEncodingException {
+ ControllerContext controllerContext = ControllerContext.getInstance();
+ controllerContext.setGlobalSchema(schemaContextModules);
+
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
+ MountService mockMountService = mock(MountService.class);
+ when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ controllerContext.setMountService(mockMountService);
+
+ String uri = createUri("/operations/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount/");
+
+ Response response = target(uri).request("application/yang.api+xml").get();
+ assertEquals(200, response.getStatus());
+ String responseBody = response.readEntity(String.class);
+ assertTrue("Xml response for /operations/mount_point rpc-behind-module1 is incorrect",
+ validateOperationsResponseXml(responseBody, "rpc-behind-module1", "module:1:behind:mount:point").find());
+ assertTrue("Xml response for /operations/mount_point rpc-behind-module2 is incorrect",
+ validateOperationsResponseXml(responseBody, "rpc-behind-module2", "module:2:behind:mount:point").find());
+
+ response = target(uri).request("application/yang.api+json").get();
+ assertEquals(200, response.getStatus());
+ 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 Matcher validateOperationsResponseJson(String searchIn, String rpcName, String moduleName) {
+ StringBuilder regex = new StringBuilder();
+ regex.append("^");
+
+ regex.append(".*\\{");
+ regex.append(".*\"");
+
+ // operations prefix optional
+ regex.append("(");
+ regex.append("ietf-restconf:");
+ regex.append("|)");
+ // :operations prefix optional
+
+ regex.append("operations\"");
+ regex.append(".*:");
+ regex.append(".*\\{");
+
+ regex.append(".*\"" + moduleName);
+ regex.append(":");
+ regex.append(rpcName + "\"");
+ regex.append(".*\\[");
+ regex.append(".*null");
+ regex.append(".*\\]");
+
+ regex.append(".*\\}");
+ regex.append(".*\\}");
+
+ regex.append(".*");
+ regex.append("$");
+ Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
+ return ptrn.matcher(searchIn);
+
+ }
+
+ private Matcher validateOperationsResponseXml(String searchIn, String rpcName, String namespace) {
+ StringBuilder regex = new StringBuilder();
+
+ regex.append("^");
+
+ regex.append(".*<operations");
+ regex.append(".*xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\"");
+ regex.append(".*>");
+
+ regex.append(".*<");
+ regex.append(".*" + rpcName);
+ regex.append(".*" + namespace);
+ regex.append(".*/");
+ regex.append(".*>");
+
+ regex.append(".*</operations.*");
+ regex.append(".*>");
+
+ regex.append(".*");
+ regex.append("$");
+ Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
+ return ptrn.matcher(searchIn);
+ }
+
+ // /restconf/modules/pathToMountPoint/yang-ext:mount
+ @Test
+ public void getModulesBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
+ ControllerContext controllerContext = ControllerContext.getInstance();
+ controllerContext.setGlobalSchema(schemaContextModules);
+
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
+ MountService mockMountService = mock(MountService.class);
+ when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ controllerContext.setMountService(mockMountService);
+
+ String uri = createUri("/modules/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount/");
+
+ Response response = target(uri).request("application/yang.api+json").get();
+ assertEquals(200, response.getStatus());
+ 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());
+ responseBody = response.readEntity(String.class);
+ assertTrue(
+ "module1-behind-mount-point in json wasn't found",
+ prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
+ .find());
+ assertTrue(
+ "module2-behind-mount-point in json wasn't found",
+ prepareXmlRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", responseBody)
+ .find());
+
+ }
+
+ // /restconf/modules/module/pathToMountPoint/yang-ext:mount/moduleName/revision
+ @Test
+ public void getModuleBehindMountPoint() throws FileNotFoundException, UnsupportedEncodingException {
+ ControllerContext controllerContext = ControllerContext.getInstance();
+ controllerContext.setGlobalSchema(schemaContextModules);
+
+ MountInstance mountInstance = mock(MountInstance.class);
+ when(mountInstance.getSchemaContext()).thenReturn(schemaContextBehindMountPoint);
+ MountService mockMountService = mock(MountService.class);
+ when(mockMountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(mountInstance);
+
+ controllerContext.setMountService(mockMountService);
+
+ String uri = createUri("/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());
+ 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());
+ 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());
+ responseBody = response.readEntity(String.class);
+ assertTrue(
+ "module1-behind-mount-point in json wasn't found",
+ prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
+ .find());
+ split = responseBody.split("<module");
+ assertEquals("<module element is returned more then once",2,split.length);
+
+
+
+
+ }
+
+ private void validateModulesResponseXml(Response response) {
+ assertEquals(200, response.getStatus());
+ String responseBody = response.readEntity(String.class);
+
+ assertTrue("Module1 in xml wasn't found", prepareXmlRegex("module1", "2014-01-01", "module:1", responseBody)
+ .find());
+ assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
+ .find());
+ assertTrue("Module3 in xml wasn't found", prepareXmlRegex("module3", "2014-01-03", "module:3", responseBody)
+ .find());
+ }
+
+ private void validateModulesResponseJson(Response response) {
+ assertEquals(200, response.getStatus());
+ 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 Matcher prepareJsonRegex(String module, String revision, String namespace, String searchIn) {
+ 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("$");
+ Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
+ return ptrn.matcher(searchIn);
+
+ }
+
+ private Matcher prepareXmlRegex(String module, String revision, String namespace, String searchIn) {
+ StringBuilder regex = new StringBuilder();
+ regex.append("^");
+
+ regex.append(".*<module.*");
+ regex.append(".*>");
+
+ regex.append(".*<name>");
+ regex.append(".*" + module);
+ regex.append(".*<\\/name>");
+
+ regex.append(".*<revision>");
+ regex.append(".*" + revision);
+ regex.append(".*<\\/revision>");
+
+ regex.append(".*<namespace>");
+ regex.append(".*" + namespace);
+ regex.append(".*<\\/namespace>");
+
+ regex.append(".*<\\/module.*>");
+
+ regex.append(".*");
+ regex.append("$");
+
+ Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
+ return ptrn.matcher(searchIn);
+ }
+
+ private void prepareMockForModulesTest(ControllerContext mockedControllerContext) throws FileNotFoundException {
+ SchemaContext schemaContext = TestUtils.loadSchemaContext("/modules");
+ mockedControllerContext.setGlobalSchema(schemaContext);
+ // when(mockedControllerContext.getGlobalSchema()).thenReturn(schemaContext);
+ }
+
private int get(String uri, String mediaType) {
return target(uri).request(mediaType).get().getStatus();
}
public class RestOperationUtils {
- static final String JSON = "+json";
- static final String XML = "+xml";
+ public static final String JSON = "+json";
+ public static final String XML = "+xml";
private RestOperationUtils() {
}
- static String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
+ public static String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
return URI.create(prefix + URLEncoder.encode(encodedPart, Charsets.US_ASCII.name()).toString()).toASCIIString();
}
}
--- /dev/null
+package org.opendaylight.controller.sal.restconf.impl.websockets.client;
+
+/**
+ * Created by mbobak on 1/22/14.
+ */
+public interface IClientMessageCallback {
+
+ public void onMessageReceived(Object message);
+}
--- /dev/null
+/*
+ * 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.net.URI;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WebSocketClient {
+
+ private final URI uri;
+ private Bootstrap bootstrap = new Bootstrap();;
+ private final WebSocketClientHandler clientHandler;
+ private static final Logger logger = LoggerFactory.getLogger(WebSocketClient.class);
+ private Channel clientChannel;
+ private final EventLoopGroup group = new NioEventLoopGroup();
+
+ public WebSocketClient(URI uri,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 (!"http".equals(protocol)) {
+ throw new IllegalArgumentException("Unsupported protocol: " + protocol);
+ }
+
+ bootstrap.group(group)
+ .channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ public void initChannel(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{
+ System.out.println("WebSocket Client connecting");
+ clientChannel = bootstrap.connect(uri.getHost(), uri.getPort()).sync().channel();
+ clientHandler.handshakeFuture().sync();
+ }
+
+ public void writeAndFlush(String message){
+ clientChannel.writeAndFlush(new TextWebSocketFrame(message));
+ }
+ public void writeAndFlush(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() throws InterruptedException {
+ clientChannel.writeAndFlush(new CloseWebSocketFrame());
+
+ // WebSocketClientHandler will close the connection when the server
+ // responds to the CloseWebSocketFrame.
+ clientChannel.closeFuture().sync();
+ group.shutdownGracefully();
+ }
+
+ public static void main(String[] args) throws Exception {
+ URI uri;
+ if (args.length > 0) {
+ uri = new URI(args[0]);
+ } else {
+ uri = new URI("http://192.168.11.1:8181/opendaylight-inventory:nodes");
+ }
+ IClientMessageCallback messageCallback = new ClientMessageCallback();
+ WebSocketClient webSocketClient = new WebSocketClient(uri, messageCallback);
+ webSocketClient.connect();
+ }
+
+ private static class ClientMessageCallback implements IClientMessageCallback {
+ @Override
+ public void onMessageReceived(Object message) {
+ logger.info("received message {}", ((TextWebSocketFrame)message).text());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.TextWebSocketFrame;
+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 logger = LoggerFactory.getLogger(WebSocketClientHandler.class.toString());
+ private final WebSocketClientHandshaker handshaker;
+ private ChannelPromise handshakeFuture;
+ private IClientMessageCallback messageListener;
+
+
+ public WebSocketClientHandler(WebSocketClientHandshaker handshaker,IClientMessageCallback listener) {
+ this.handshaker = handshaker;
+ this.messageListener = listener;
+ }
+
+ public ChannelFuture handshakeFuture() {
+ return handshakeFuture;
+ }
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ handshakeFuture = ctx.newPromise();
+ }
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ handshaker.handshake(ctx.channel());
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ logger.info("WebSocket Client disconnected!");
+ }
+
+ @Override
+ public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
+ Channel ch = ctx.channel();
+ if (!handshaker.isHandshakeComplete()) {
+ handshaker.finishHandshake(ch, (FullHttpResponse) msg);
+ logger.info("WebSocket Client connected!");
+ handshakeFuture.setSuccess();
+ return;
+ }
+
+ if (msg instanceof FullHttpResponse) {
+ FullHttpResponse response = (FullHttpResponse) msg;
+ throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content="
+ + response.content().toString(CharsetUtil.UTF_8) + ')');
+ }
+
+ messageListener.onMessageReceived(msg);
+ WebSocketFrame frame = (WebSocketFrame) msg;
+
+ if (frame instanceof PongWebSocketFrame) {
+ logger.info("WebSocket Client received pong");
+ } else if (frame instanceof CloseWebSocketFrame) {
+ logger.info("WebSocket Client received closing");
+ ch.close();
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ cause.printStackTrace();
+
+ if (!handshakeFuture.isDone()) {
+ handshakeFuture.setFailure(cause);
+ }
+
+ ctx.close();
+ }
+}
+
--- /dev/null
+/*
+ * 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 static org.opendaylight.controller.sal.restconf.impl.test.RestOperationUtils.createUri;
+
+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.Test;
+import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
+import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class RestStream extends JerseyTest {
+
+ private static BrokerFacade brokerFacade;
+ private static RestconfImpl restconfImpl;
+ private static SchemaContext schemaContextYangsIetf;
+
+ @BeforeClass
+ public static void init() throws FileNotFoundException {
+ schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs");
+ ControllerContext controllerContext = ControllerContext.getInstance();
+ controllerContext.setSchemas(schemaContextYangsIetf);
+ brokerFacade = mock(BrokerFacade.class);
+ restconfImpl = RestconfImpl.getInstance();
+ restconfImpl.setBroker(brokerFacade);
+ restconfImpl.setControllerContext(controllerContext);
+ }
+
+ @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(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
+ StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
+ JsonToCompositeNodeProvider.INSTANCE);
+ return resourceConfig;
+ }
+
+ @Test
+ public void testCallRpcCallGet() throws UnsupportedEncodingException, InterruptedException {
+ String uri = createUri("/operations/", "sal-remote:create-data-change-event-subscription");
+ Response responseWithStreamName = post(uri, MediaType.APPLICATION_XML, getRpcInput());
+ String xmlResponse = responseWithStreamName.readEntity(String.class);
+ assertNotNull(xmlResponse);
+ assertTrue(xmlResponse.contains("<stream-name>ietf-interfaces:interfaces/ietf-interfaces:interface/eth0</stream-name>"));
+
+ uri = createUri("/streams/stream/", "ietf-interfaces:interfaces/ietf-interfaces:interface/eth0");
+ Response responseWithRedirectionUri = get(uri, MediaType.APPLICATION_XML);
+ final URI websocketServerUri = responseWithRedirectionUri.getLocation();
+ assertNotNull(websocketServerUri);
+ assertEquals(websocketServerUri.toString(), "http://localhost:8181/ietf-interfaces:interfaces/ietf-interfaces:interface/eth0");
+ }
+
+ private Response post(String uri, String mediaType, String data) {
+ return target(uri).request(mediaType).post(Entity.entity(data, mediaType));
+ }
+
+ private Response get(String uri, String mediaType) {
+ return target(uri).request(mediaType).get();
+ }
+
+ private String getRpcInput() {
+ 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();
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+ 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)";
+ }
+
+ }
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+ 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";
+ }
+
+ }
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+module module3 {
+ namespace "module:3";
+ prefix "mod3";
+ revision "2014-01-03";
+}
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+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