Introduce IOS-XE (Netconf) renderer for SFC 02/38402/13
authorVladimir Lavor <[email protected]>
Tue, 10 May 2016 13:30:30 +0000 (15:30 +0200)
committerBrady Johnson <[email protected]>
Wed, 1 Jun 2016 07:30:20 +0000 (07:30 +0000)
Signed-off-by: Vladimir Lavor <[email protected]>
Change-Id: I3ee145fa0a4e5e9d4da7035f7d47d1add5ee011c

30 files changed:
features/pom.xml
features/src/main/features/features.xml
sfc-renderers/pom.xml
sfc-renderers/sfc-ios-xe-renderer/pom.xml [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModule.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModuleFactory.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/SfcIosXeRenderer.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/NodeListener.java [new file with mode: 0755]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/RenderedPathListener.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceForwarderListener.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceFunctionListener.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessor.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapper.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapper.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManager.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPI.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtils.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/yang/gen/v1/http/tail/f/com/yang/common/rev150522/IpAddressAndPrefixLengthBuilder.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/resources/configuration/initial/sfc-ios-xe-initial.xml [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/ned.yang [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-cli-extensions.yang [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-common.yang [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-meta-extensions.yang [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/main/yang/sfc-ios-xe-impl.yang [new file with mode: 0755]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessorTest.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapperTest.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapperTest.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManagerTest.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPITest.java [new file with mode: 0644]
sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtilsTest.java [new file with mode: 0644]

index 6d09125eea3014b53233e4ac1867e13810177251..9cd1b2f3d2b4af0b5ac6ac64b5d7c188e64d1feb 100644 (file)
         <artifactId>sfc-scf-openflow</artifactId>
         <version>${project.version}</version>
     </dependency>
+    <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>sfc-ios-xe-renderer</artifactId>
+        <version>${project.version}</version>
+    </dependency>
     <dependency>
         <groupId>${project.groupId}</groupId>
         <artifactId>sfc-openflow-utils</artifactId>
        <type>xml</type>
        <classifier>config</classifier>
     </dependency>
+    <!-- odl-ios-xe-renderer config files -->
+    <dependency>
+       <groupId>org.opendaylight.sfc</groupId>
+       <artifactId>sfc-ios-xe-renderer</artifactId>
+       <version>${project.version}</version>
+       <type>xml</type>
+       <classifier>config</classifier>
+    </dependency>
     <!-- odl-sfc-ovs config files -->
     <dependency>
        <groupId>${project.groupId}</groupId>
index b7b6af3e09b6ec08c68c8e10b426325fb547622d..ee44b55c60d132f8e2bf0b56d06fdce071e19cc8 100644 (file)
@@ -26,7 +26,7 @@
 
     <feature name="odl-sfc-model" version="${project.version}" description="OpenDaylight :: sfc :: Model">
         <feature version="${mdsal.model.version}">odl-mdsal-models</feature>
-         <feature version="${ovsdb.southbound.version}">odl-ovsdb-southbound-api</feature>
+        <feature version="${ovsdb.southbound.version}">odl-ovsdb-southbound-api</feature>
         <bundle>mvn:org.opendaylight.sfc/sfc-model/{{VERSION}}</bundle>
     </feature>
 
     </feature>
 
     <feature name="odl-sfc-provider-rest" version="${project.version}" description="OpenDaylight :: sfc :: Provider ">
-      <feature version="${project.version}">odl-sfc-provider</feature>
-      <feature version="${restconf.version}">odl-restconf</feature>
+        <feature version="${project.version}">odl-sfc-provider</feature>
+        <feature version="${restconf.version}">odl-restconf</feature>
     </feature>
 
-     <feature name="odl-sfc-netconf" version="${project.version}" description="OpenDaylight :: Netconf ">
+    <feature name="odl-sfc-netconf" version="${project.version}" description="OpenDaylight :: Netconf ">
         <feature version="${project.version}">odl-sfc-provider-rest</feature>
         <feature version="${netconf.parent.version}">odl-netconf-connector</feature>
         <bundle>mvn:org.opendaylight.sfc/sfc-netconf/{{VERSION}}</bundle>
         <configfile finalname="etc/opendaylight/karaf/sfc-netconf-initial.xml">mvn:org.opendaylight.sfc/sfc-netconf/{{VERSION}}/xml/config</configfile>
     </feature>
 
+    <feature name="odl-sfc-ios-xe-renderer" version="${project.version}" description="OpenDaylight :: sfc-renderers :: IOS-XE Renderer ">
+        <feature version="${project.version}">odl-sfc-provider-rest</feature>
+        <feature version="${netconf.parent.version}">odl-netconf-connector</feature>
+        <bundle>mvn:org.opendaylight.sfc/sfc-ios-xe-renderer/{{VERSION}}</bundle>
+        <configfile finalname="etc/opendaylight/karaf/sfc-ios-xe-renderer-initial.xml">mvn:org.opendaylight.sfc/sfc-ios-xe-renderer/{{VERSION}}/xml/config</configfile>
+    </feature>
+
     <feature name="odl-sfc-ovs" version="${project.version}" description="OpenDaylight :: OpenvSwitch ">
         <feature version="${project.version}">odl-sfc-provider-rest</feature>
         <feature version="${ovsdb.southbound.version}">odl-ovsdb-southbound-impl</feature>
index d281fdb449f6a3984962dbab95edd387bf9c2368..e0a8e9f6c65ccd32714d4e0931a289cf6982d9de 100644 (file)
@@ -17,8 +17,7 @@
     <description>This module groups together the RSP renderers.</description>
 
     <modules>
-        <!-- <module>sfc-foo-renderer</module> -->
-        <!-- <module>sfc-bar-renderer</module> -->
+        <module>sfc-ios-xe-renderer</module>
         <module>sfc-openflow-renderer</module>
     </modules>
 
diff --git a/sfc-renderers/sfc-ios-xe-renderer/pom.xml b/sfc-renderers/sfc-ios-xe-renderer/pom.xml
new file mode 100644 (file)
index 0000000..7775b03
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.sfc</groupId>
+        <artifactId>sfc-renderers</artifactId>
+        <version>0.3.0-SNAPSHOT</version>
+        <relativePath>../</relativePath>
+    </parent>
+
+    <artifactId>sfc-ios-xe-renderer</artifactId>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <configfile>configuration/initial/sfc-ios-xe-initial.xml</configfile>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>sfc-provider</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.netconf</groupId>
+            <artifactId>sal-netconf-connector</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal.model</groupId>
+            <artifactId>ietf-inet-types-2013-07-15</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-artifacts</id>
+                        <goals>
+                            <goal>attach-artifact</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <artifacts>
+                                <artifact>
+                                    <file>${project.build.directory}/classes/${configfile}</file>
+                                    <type>xml</type>
+                                    <classifier>config</classifier>
+                                </artifact>
+                            </artifacts>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModule.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModule.java
new file mode 100644 (file)
index 0000000..08410ed
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.yang.config.sfc_ios_xe.impl;
+
+import org.opendaylight.sfc.sfc_ios_xe.provider.SfcIosXeRenderer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SfcIosXeModule extends org.opendaylight.controller.config.yang.config.sfc_ios_xe.impl.AbstractSfcIosXeModule {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SfcIosXeModule.class);
+
+    public SfcIosXeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public SfcIosXeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.sfc_ios_xe.impl.SfcIosXeModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void customValidation() {
+        // add custom validation form module attributes here.
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        LOG.info("Initializing SFC IOS-XE renderer module ... ");
+
+        SfcIosXeRenderer renderer = new SfcIosXeRenderer(getDataBrokerDependency(), getBindingRegistryDependency());
+
+        LOG.info("SFC IOS-XE renderer module initialized");
+
+        final class AutoCloseableSfcOvs implements AutoCloseable {
+
+            @Override
+            public void close() {
+                renderer.unregisterListeners();
+                LOG.info("IOS-XE renderer listeners closed");
+            }
+        }
+
+        return new AutoCloseableSfcOvs();
+    }
+
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModuleFactory.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/controller/config/yang/config/sfc_ios_xe/impl/SfcIosXeModuleFactory.java
new file mode 100644 (file)
index 0000000..38763e0
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.config.yang.config.sfc_ios_xe.impl;
+
+public class SfcIosXeModuleFactory extends org.opendaylight.controller.config.yang.config.sfc_ios_xe.impl.AbstractSfcIosXeModuleFactory {
+
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/SfcIosXeRenderer.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/SfcIosXeRenderer.java
new file mode 100644 (file)
index 0000000..a3c7f59
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeRspProcessor;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeServiceForwarderMapper;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeServiceFunctionMapper;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.NodeManager;
+
+/**
+ * Initialize all necessary IOS-XE renderer components
+ */
+public class SfcIosXeRenderer {
+
+    private final NodeManager nodeManager;
+    private final IosXeServiceForwarderMapper forwarderMapper;
+    private final IosXeServiceFunctionMapper functionMapper;
+    private final IosXeRspProcessor rspProcessor;
+
+    public SfcIosXeRenderer(DataBroker dataBroker, BindingAwareBroker bindingAwareBroker) {
+        nodeManager = new NodeManager(dataBroker, bindingAwareBroker);
+        forwarderMapper = new IosXeServiceForwarderMapper(dataBroker, nodeManager);
+        functionMapper = new IosXeServiceFunctionMapper(dataBroker, nodeManager);
+        rspProcessor = new IosXeRspProcessor(dataBroker, nodeManager);
+    }
+
+    public void unregisterListeners() {
+        nodeManager.unregisterNodeListener();
+        forwarderMapper.unregisterSffListener();
+        functionMapper.unregisterSfListener();
+        rspProcessor.unregisterRspListener();
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/NodeListener.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/NodeListener.java
new file mode 100755 (executable)
index 0000000..7721a67
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/**
+ * DataChangeListener attached to the Network Topology
+ */
+package org.opendaylight.sfc.sfc_ios_xe.provider.listener;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.NodeManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilities;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+
+import static org.opendaylight.sfc.provider.SfcProviderDebug.printTraceStart;
+
+public class NodeListener implements DataTreeChangeListener<Node> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NodeListener.class);
+
+    private final ListenerRegistration listenerRegistration;
+    private final NodeManager nodeManager;
+
+    public NodeListener(DataBroker dataBroker, NodeManager nodeManager) {
+        this.nodeManager = nodeManager;
+        // Register listener
+        listenerRegistration = dataBroker
+                .registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
+                        InstanceIdentifier.builder(NetworkTopology.class)
+                                .child(Topology.class)
+                                .child(Node.class).build()), this);
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Node>> changes) {
+        printTraceStart(LOG);
+        for (DataTreeModification<Node> modification : changes) {
+            DataObjectModification<Node> rootNode = modification.getRootNode();
+            switch (rootNode.getModificationType()) {
+                case WRITE:
+                case SUBTREE_MODIFIED:
+                    if (rootNode.getDataAfter() != null) {
+                        Node node = rootNode.getDataAfter();
+                        if (isCapableNetconfDevice(node)) {
+                            nodeManager.updateNode(node);
+                        }
+                        break;
+                    }
+                case DELETE:
+                    if (rootNode.getDataBefore() != null) {
+                        Node node = rootNode.getDataBefore();
+                        if (isCapableNetconfDevice(node)) {
+                            nodeManager.removeNode(node);
+                        }
+                    }
+            }
+        }
+    }
+
+    private boolean isCapableNetconfDevice(Node node) {
+        NetconfNode netconfAugmentation = node.getAugmentation(NetconfNode.class);
+        if (netconfAugmentation == null) {
+            LOG.debug("Node {} is not a netconf device", node.getNodeId().getValue());
+            return false;
+        }
+        AvailableCapabilities capabilities = netconfAugmentation.getAvailableCapabilities();
+        // TODO maybe add more specific capability test
+        return capabilities.getAvailableCapability()
+                .contains("urn:ietf:params:netconf:capability:writable-running:1.0");
+    }
+
+    public ListenerRegistration getRegistrationObject() {
+        return listenerRegistration;
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/RenderedPathListener.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/RenderedPathListener.java
new file mode 100644 (file)
index 0000000..1e539a1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.listener;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeRspProcessor;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+
+public class RenderedPathListener implements DataTreeChangeListener<RenderedServicePaths> {
+
+    private final IosXeRspProcessor rspProcessor;
+    private final ListenerRegistration iosXeRspListenerRegistration;
+
+    public RenderedPathListener(DataBroker dataBroker, IosXeRspProcessor rspProcessor) {
+        this.rspProcessor = rspProcessor;
+        // Register listener
+        iosXeRspListenerRegistration = dataBroker
+                .registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
+                        InstanceIdentifier.builder(RenderedServicePaths.class).build()), this);
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<RenderedServicePaths>> changes) {
+        for (DataTreeModification<RenderedServicePaths> modification : changes) {
+            DataObjectModification<RenderedServicePaths> rootNode = modification.getRootNode();
+
+            switch (rootNode.getModificationType()) {
+                case WRITE:
+                case SUBTREE_MODIFIED:
+                    if (rootNode.getDataAfter() != null && rootNode.getDataAfter().getRenderedServicePath() != null) {
+                        rootNode.getDataAfter().getRenderedServicePath().forEach(rspProcessor::updateRsp);
+                    }
+                    break;
+                case DELETE:
+                    if (rootNode.getDataBefore() != null && rootNode.getDataBefore().getRenderedServicePath() != null) {
+                        rootNode.getDataBefore().getRenderedServicePath().forEach(rspProcessor::deleteRsp);
+                    }
+                    break;
+            }
+        }
+    }
+
+    public ListenerRegistration getRegistrationObject() {
+        return iosXeRspListenerRegistration;
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceForwarderListener.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceForwarderListener.java
new file mode 100644 (file)
index 0000000..b7c4a13
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.listener;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeServiceForwarderMapper;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.ServiceFunctionForwarders;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+
+public class ServiceForwarderListener implements DataTreeChangeListener<ServiceFunctionForwarders> {
+
+    private final ListenerRegistration iosXeSffListenerRegistration;
+    private final IosXeServiceForwarderMapper sffManager;
+
+    public ServiceForwarderListener(DataBroker dataBroker, IosXeServiceForwarderMapper sffManager) {
+        this.sffManager = sffManager;
+        // Register listener
+        iosXeSffListenerRegistration = dataBroker
+                .registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                        InstanceIdentifier.builder(ServiceFunctionForwarders.class).build()), this);
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<ServiceFunctionForwarders>> changes) {
+        for (DataTreeModification<ServiceFunctionForwarders> modification : changes) {
+            DataObjectModification<ServiceFunctionForwarders> rootNode = modification.getRootNode();
+
+            switch (rootNode.getModificationType()) {
+                case WRITE:
+                case SUBTREE_MODIFIED:
+                    if (rootNode.getDataAfter() != null
+                            && rootNode.getDataAfter().getServiceFunctionForwarder() != null) {
+                        sffManager.syncForwarders(rootNode.getDataAfter().getServiceFunctionForwarder(), false);
+                    }
+                    break;
+                case DELETE:
+                    if (rootNode.getDataBefore() != null
+                            && rootNode.getDataBefore().getServiceFunctionForwarder() != null) {
+                        sffManager.syncForwarders(rootNode.getDataBefore().getServiceFunctionForwarder(), true);
+                    }
+                    break;
+            }
+        }
+    }
+
+    public ListenerRegistration getRegistrationObject() {
+        return iosXeSffListenerRegistration;
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceFunctionListener.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/listener/ServiceFunctionListener.java
new file mode 100644 (file)
index 0000000..0fbcc50
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.listener;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.renderer.IosXeServiceFunctionMapper;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.ServiceFunctions;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+
+public class ServiceFunctionListener implements DataTreeChangeListener<ServiceFunctions> {
+
+    private final IosXeServiceFunctionMapper sfManager;
+    private final ListenerRegistration iosXeSfListenerRegistration;
+
+    public ServiceFunctionListener(DataBroker dataBroker, IosXeServiceFunctionMapper sfManager) {
+        this.sfManager = sfManager;
+        // Register listener
+        iosXeSfListenerRegistration = dataBroker
+                .registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION,
+                        InstanceIdentifier.builder(ServiceFunctions.class).build()), this);
+    }
+
+    @Override
+    public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<ServiceFunctions>> changes) {
+        for (DataTreeModification<ServiceFunctions> modification : changes) {
+            DataObjectModification<ServiceFunctions> rootNode = modification.getRootNode();
+
+            switch (rootNode.getModificationType()) {
+                case WRITE:
+                case SUBTREE_MODIFIED:
+                    if (rootNode.getDataAfter() != null && rootNode.getDataAfter().getServiceFunction() != null) {
+                        sfManager.syncFunctions(rootNode.getDataAfter().getServiceFunction(), false);
+                    }
+                    break;
+                case DELETE:
+                    if (rootNode.getDataBefore() != null && rootNode.getDataBefore().getServiceFunction() != null) {
+                        sfManager.syncFunctions(rootNode.getDataBefore().getServiceFunction(), true);
+                    }
+                    break;
+            }
+        }
+    }
+
+    public ListenerRegistration getRegistrationObject() {
+        return iosXeSfListenerRegistration;
+    }
+
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessor.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessor.java
new file mode 100644 (file)
index 0000000..2b2cc22
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
+import org.opendaylight.sfc.sfc_ios_xe.provider.listener.RenderedPathListener;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.SfcIosXeUtils;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.Services;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.ServicesKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.ServiceTypeChoice;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.service.type.choice.ServiceFunctionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.service.type.choice.ServiceFunctionForwarderBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.service.index.services.service.type.choice.TerminateBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_PATH;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.READ_FUNCTION;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_PATH;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_REMOTE;
+
+public class IosXeRspProcessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IosXeRspProcessor.class);
+
+    private final NodeManager nodeManager;
+    private final RenderedPathListener rspListener;
+
+    public IosXeRspProcessor(DataBroker dataBroker, NodeManager nodeManager) {
+        this.nodeManager = nodeManager;
+        // Register RSP listener
+        rspListener = new RenderedPathListener(dataBroker, this);
+    }
+
+    public void updateRsp(RenderedServicePath renderedServicePath) {
+        Preconditions.checkNotNull(renderedServicePath);
+        Long pathId = renderedServicePath.getPathId();
+        Short serviceIndex = renderedServicePath.getStartingIndex();
+
+        DataBroker previousMountPoint;
+        DataBroker currentMountpoint;
+        SffName previousSffName;
+        SffName currentSffName;
+        if (renderedServicePath.getRenderedServicePathHop() == null ||
+                renderedServicePath.getRenderedServicePathHop().isEmpty()) {
+            LOG.warn("Rendered path {} does not contain any hop", renderedServicePath.getName().getValue());
+            return;
+        }
+        Iterator<RenderedServicePathHop> rspHopIterator = renderedServicePath.getRenderedServicePathHop()
+                .iterator();
+        // Proceed first hop in Rsp. Service Type choice for first hop is always Service Function
+        RenderedServicePathHop hop = rspHopIterator.next();
+        currentSffName = hop.getServiceFunctionForwarder();
+        currentMountpoint = getSffMountpoint(currentSffName);
+        if (currentMountpoint == null) {
+            LOG.error("Resolving of RSP {} failed, mountpoint for SFF {} is null", renderedServicePath.getName()
+                    .getValue(), currentSffName.getValue());
+            deleteRsp(renderedServicePath);
+            return;
+        }
+        // New list of services has to be created every time new mountpoint is created
+        List<Services> services = new ArrayList<>();
+        SfName sfName = hop.getServiceFunctionName();
+        ServiceFunction serviceFunction = (ServiceFunction) new IosXeDataStoreAPI(currentMountpoint,
+                sfName, READ_FUNCTION, LogicalDatastoreType.OPERATIONAL).call();
+        ServiceTypeChoice serviceTypeChoice = buildServiceFunctionChoice(serviceFunction);
+        Services serviceEntry = createServicesEntry(serviceIndex, serviceTypeChoice);
+        services.add(serviceEntry);
+        serviceIndex--;
+        while (rspHopIterator.hasNext()) {
+            hop = rspHopIterator.next();
+            // Find out whether next hop SF is connected to the same SFF
+            previousSffName = currentSffName;
+            currentSffName = hop.getServiceFunctionForwarder();
+            if (previousSffName.equals(currentSffName)) {
+                // Next hop SF is on the same local SFF/node as the previous one
+                sfName = hop.getServiceFunctionName();
+                serviceFunction = (ServiceFunction) new IosXeDataStoreAPI(currentMountpoint,
+                        sfName, READ_FUNCTION, LogicalDatastoreType.OPERATIONAL).call();
+                serviceTypeChoice = buildServiceFunctionChoice(serviceFunction);
+                serviceEntry = createServicesEntry(serviceIndex, serviceTypeChoice);
+                services.add(serviceEntry);
+                serviceIndex--;
+            } else {
+                // Next hop SF is on different node. Store previous SFF and its mountpoint
+                previousMountPoint = currentMountpoint;
+                currentSffName = hop.getServiceFunctionForwarder();
+                currentMountpoint = getSffMountpoint(currentSffName);
+                if (currentMountpoint == null) {
+                    LOG.error("Resolving of RSP {} failed, mountpoint for SFF {} is null", renderedServicePath.getName()
+                            .getValue(), currentSffName.getValue());
+                    deleteRsp(renderedServicePath);
+                    return;
+                }
+                // Write current SFF to previous SFF node as remote
+                ServiceFfName currentRemoteForwarder = SfcIosXeUtils.createRemoteForwarder(currentSffName);
+                if (currentRemoteForwarder == null) {
+                    LOG.error("SFF {} ip address is null", currentSffName.getValue());
+                    deleteRsp(renderedServicePath);
+                    return;
+                }
+                new IosXeDataStoreAPI(previousMountPoint, currentRemoteForwarder, WRITE_REMOTE,
+                        LogicalDatastoreType.CONFIGURATION).call();
+                // Create last service entry to previous node which sends traffic to current node
+                serviceTypeChoice = buildServiceFunctionForwarderChoice(currentSffName.getValue());
+                serviceEntry = createServicesEntry(serviceIndex, serviceTypeChoice);
+                services.add(serviceEntry);
+                // List of services completed for last mountpoint, create service path entries and write it
+                ServicePath servicePath = createServicePath(pathId, services);
+                new IosXeDataStoreAPI(previousMountPoint, servicePath, WRITE_PATH,
+                        LogicalDatastoreType.CONFIGURATION).call();
+                // Start with new services list
+                services = new ArrayList<>();
+                sfName = hop.getServiceFunctionName();
+                serviceFunction = (ServiceFunction) new IosXeDataStoreAPI(currentMountpoint,
+                        sfName, READ_FUNCTION, LogicalDatastoreType.OPERATIONAL).call();
+                serviceTypeChoice = buildServiceFunctionChoice(serviceFunction);
+                serviceEntry = createServicesEntry(serviceIndex, serviceTypeChoice);
+                services.add(serviceEntry);
+                serviceIndex--;
+            }
+        }
+        // Proceed last entry (it's the same hop as the previous one using same mountpoint and list of services)
+        // Service Type choice is always Terminate
+        serviceTypeChoice = buildTerminateChoice();
+        serviceEntry = createServicesEntry(serviceIndex, serviceTypeChoice);
+        services.add(serviceEntry);
+        // List of services completed for last mountpoint, create last service path entries and write it
+        ServicePath servicePath = createServicePath(pathId, services);
+        new IosXeDataStoreAPI(currentMountpoint, servicePath, WRITE_PATH, LogicalDatastoreType.CONFIGURATION).call();
+        LOG.info("Rendered service path {} successfully processed", renderedServicePath.getName().getValue());
+
+    }
+
+    public void deleteRsp(RenderedServicePath renderedServicePath) {
+        boolean success = true;
+        long pathId = renderedServicePath.getPathId();
+        ServicePathKey servicePathKey = new ServicePathKey(pathId);
+        for (RenderedServicePathHop renderedServicePathHop : renderedServicePath.getRenderedServicePathHop()) {
+            SffName sffName = renderedServicePathHop.getServiceFunctionForwarder();
+            DataBroker sffDataBroker = getSffMountpoint(sffName);
+            boolean transaction = (boolean) new IosXeDataStoreAPI(sffDataBroker, servicePathKey, DELETE_PATH,
+                    LogicalDatastoreType.CONFIGURATION).call();
+            if (!transaction) {
+                success = false;
+            }
+        }
+        if (success) {
+            LOG.info("Service path {} removed", pathId);
+        } else {
+            LOG.error("Failed to remove service path {}", pathId);
+        }
+    }
+
+    private ServiceTypeChoice buildServiceFunctionForwarderChoice(String sffName) {
+        ServiceFunctionForwarderBuilder serviceFunctionForwarderBuilder = new ServiceFunctionForwarderBuilder();
+        serviceFunctionForwarderBuilder.setServiceFunctionForwarder(sffName);
+        return serviceFunctionForwarderBuilder.build();
+    }
+
+    private ServiceTypeChoice buildServiceFunctionChoice(ServiceFunction serviceFunction) {
+        ServiceFunctionBuilder serviceFunctionTypeChoice = new ServiceFunctionBuilder();
+        serviceFunctionTypeChoice.setServiceFunction(serviceFunction.getName());
+        return serviceFunctionTypeChoice.build();
+    }
+
+    private ServiceTypeChoice buildTerminateChoice() {
+        TerminateBuilder terminateBuilder = new TerminateBuilder();
+        terminateBuilder.setTerminate(true);
+        return terminateBuilder.build();
+    }
+
+    private ServicePath createServicePath(Long pathId, List<Services> services) {
+        // Service Index
+        ServiceIndexBuilder serviceIndexBuilder = new ServiceIndexBuilder();
+        serviceIndexBuilder.setServices(services);
+        // Service Chain Path Mode
+        ConfigServiceChainPathModeBuilder pathModeBuilder = new ConfigServiceChainPathModeBuilder();
+        pathModeBuilder.setServiceIndex(serviceIndexBuilder.build());
+        // Service Path
+        ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
+        servicePathBuilder.setKey(new ServicePathKey(pathId))
+                .setServicePathId(pathId)
+                .setConfigServiceChainPathMode(pathModeBuilder.build());
+        return servicePathBuilder.build();
+    }
+
+    private Services createServicesEntry(short index, ServiceTypeChoice choice) {
+        ServicesBuilder servicesBuilder = new ServicesBuilder();
+        servicesBuilder.setKey(new ServicesKey(index))
+                .setServiceIndexId(index)
+                .setServiceTypeChoice(choice);
+        return servicesBuilder.build();
+    }
+
+    private DataBroker getSffMountpoint(SffName sffName) {
+        // Read SFF from Controller CONF
+        org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder sfcForwarder =
+                SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName);
+        if (sfcForwarder == null) {
+            LOG.error("SFF name {} not found in data store", sffName.getValue());
+            return null;
+        }
+        IpAddress sffMgmtIp = sfcForwarder.getIpMgmtAddress();
+        if (sffMgmtIp == null) {
+            LOG.error("Unable to obtain management IP for SFF {}", sffName.getValue());
+            return null;
+        }
+        return nodeManager.getMountpointFromIpAddress(new IpAddress(new Ipv4Address(sffMgmtIp.getIpv4Address()
+                .getValue())));
+    }
+
+    public void unregisterRspListener() {
+        rspListener.getRegistrationObject().close();
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapper.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapper.java
new file mode 100644 (file)
index 0000000..943fe87
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.listener.ServiceForwarderListener;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.SfcIosXeUtils;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_LOCAL;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_LOCAL;
+
+
+public class IosXeServiceForwarderMapper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IosXeServiceForwarderMapper.class);
+
+    private final NodeManager nodeManager;
+    private final ServiceForwarderListener sffListener;
+
+    public IosXeServiceForwarderMapper(DataBroker dataBroker, NodeManager nodeManager) {
+        this.nodeManager = nodeManager;
+        // Register SFF listener
+        sffListener = new ServiceForwarderListener(dataBroker, this);
+    }
+
+    public void syncForwarders(List<ServiceFunctionForwarder> forwarders, boolean delete) {
+        for (ServiceFunctionForwarder forwarder : forwarders) {
+            IpAddress forwarderMgmtIp = forwarder.getIpMgmtAddress();
+            if (forwarderMgmtIp == null) {
+                LOG.warn("Service function forwarder {} has no management Ip address, cannot be created",
+                        forwarder.getName().getValue());
+                continue;
+            }
+            // Find appropriate node for SFF
+            for (Node netconfNode : nodeManager.getConnectedNodes().values()) {
+                IpAddress netconfNodeIp = nodeManager.getNetconfNodeIp(netconfNode);
+                if (netconfNodeIp.equals(forwarderMgmtIp)) {
+                    // Find the right mountpoint
+                    DataBroker mountPoint = nodeManager.getActiveMountPoints()
+                            .get(netconfNode.getNodeId());
+                    if (mountPoint != null) {
+                        for (SffDataPlaneLocator forwarderDpl : forwarder.getSffDataPlaneLocator()) {
+                            DataPlaneLocator dpl = forwarderDpl.getDataPlaneLocator();
+                            LocatorType locatorType = dpl.getLocatorType();
+                            Ip sffIp = null;
+                            if (locatorType instanceof Ip) {
+                                LOG.debug("IP locator found: {} ", locatorType);
+                                sffIp = (Ip) locatorType;
+                            }
+                            if (sffIp != null && sffIp.getIp() != null) {
+                                IpAddress ipAddress = sffIp.getIp();
+                                // Create/remove local SFF
+                                org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder localForwarder =
+                                        SfcIosXeUtils.createLocalForwarder(ipAddress);
+                                if (localForwarder != null && !delete) {
+                                    new IosXeDataStoreAPI(mountPoint, localForwarder.getLocal(), WRITE_LOCAL,
+                                            LogicalDatastoreType.CONFIGURATION).call();
+                                    LOG.info("Local forwarder with ip {} created on node {}",
+                                            forwarder.getIpMgmtAddress().toString(), netconfNode.getNodeId().getValue());
+                                }
+                                if (localForwarder != null && delete) {
+                                    new IosXeDataStoreAPI(mountPoint, localForwarder.getLocal(), DELETE_LOCAL,
+                                            LogicalDatastoreType.CONFIGURATION).call();
+                                    LOG.info("Local forwarder removed from node {}", netconfNode.getNodeId().getValue());
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    LOG.warn("Node not found for SFF {}", forwarder.getName());
+                }
+            }
+        }
+    }
+
+    public void unregisterSffListener() {
+        sffListener.getRegistrationObject().close();
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapper.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapper.java
new file mode 100644 (file)
index 0000000..3d1fce1
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.sfc_ios_xe.provider.listener.ServiceFunctionListener;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.SfcIosXeUtils;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.SlTransportType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.ConfigServiceChainSfModeBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.Encapsulation;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.EncapsulationBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.encapsulation.Gre;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.encapsulation.GreBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_FUNCTION;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_FUNCTION;
+
+public class IosXeServiceFunctionMapper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(IosXeServiceFunctionMapper.class);
+
+    private final NodeManager nodeManager;
+    private final ServiceFunctionListener sfListener;
+
+    public IosXeServiceFunctionMapper(DataBroker dataBroker, NodeManager nodeManager) {
+        this.nodeManager = nodeManager;
+        // Register SF listener
+        sfListener = new ServiceFunctionListener(dataBroker, this);
+    }
+
+    public void syncFunctions(List<ServiceFunction> functions, boolean delete) {
+        for (ServiceFunction function : functions) {
+            IpAddress forwarderMgmtIp = function.getIpMgmtAddress();
+            if (forwarderMgmtIp == null) {
+                LOG.warn("Service function forwarder {} has no management Ip address, cannot be created",
+                        function.getName().getValue());
+                continue;
+            }
+            // Find appropriate node for SFF
+            for (Node netconfNode : nodeManager.getConnectedNodes().values()) {
+                IpAddress netconfNodeIp = nodeManager.getNetconfNodeIp(netconfNode);
+                if (netconfNodeIp.equals(forwarderMgmtIp)) {
+                    // Find the right mountpoint
+                    DataBroker mountPoint = nodeManager.getActiveMountPoints()
+                            .get(netconfNode.getNodeId());
+                    if (mountPoint != null && !delete) {
+                        org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction serviceFunction =
+                                createNetconfServiceFunction(function);
+                        if (serviceFunction != null) {
+                            new IosXeDataStoreAPI(mountPoint, serviceFunction, WRITE_FUNCTION,
+                                    LogicalDatastoreType.CONFIGURATION).call();
+                            LOG.info("Service function {} created on node {}", serviceFunction.getName(),
+                                    netconfNode.getNodeId().getValue());
+                        }
+                    }
+                    if (mountPoint != null && delete) {
+                        org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction serviceFunction =
+                                createNetconfServiceFunction(function);
+                        if (serviceFunction != null) {
+                            new IosXeDataStoreAPI(mountPoint, serviceFunction.getKey(), DELETE_FUNCTION,
+                                    LogicalDatastoreType.CONFIGURATION).call();
+                            LOG.info("Service function {} removed", serviceFunction.getName());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction createNetconfServiceFunction(
+            ServiceFunction function) {
+        SfName sfName = function.getName();
+
+        SfDataPlaneLocator sfDataPlaneLocator = SfcIosXeUtils.getDplWithIpLocatorType(function
+                .getSfDataPlaneLocator());
+        if (sfDataPlaneLocator == null || sfDataPlaneLocator.getLocatorType() == null) {
+            LOG.warn("Any suitable data plane locator has not been found for service function {}", function.getName()
+                    .getValue());
+            return null;
+        }
+        IpAddress sfDplIpAddress = null;
+        LocatorType locatorType = sfDataPlaneLocator.getLocatorType();
+        if (locatorType instanceof Ip) {
+            sfDplIpAddress = ((Ip) locatorType).getIp();
+        }
+        if (sfDplIpAddress != null && sfDplIpAddress.getIpv4Address() != null) {
+            // Ip Address
+            IpBuilder ipBuilder = new IpBuilder();
+            ipBuilder.setAddress(new Ipv4Address(sfDplIpAddress.getIpv4Address().getValue()));
+            // Encapsulation
+            Class<? extends SlTransportType> transport = sfDataPlaneLocator.getTransport();
+            if (transport == org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Gre.class) {
+                ConfigServiceChainSfModeBuilder sfModeBuilder = new ConfigServiceChainSfModeBuilder();
+                sfModeBuilder.setIp(ipBuilder.build())
+                        .setEncapsulation(buildSfEncapsulation());
+                ServiceFunctionBuilder netconfServiceFunction = new ServiceFunctionBuilder();
+                netconfServiceFunction.setName(sfName.getValue())
+                        .setKey(new ServiceFunctionKey(sfName.getValue()))
+                        .setConfigServiceChainSfMode(sfModeBuilder.build());
+                return netconfServiceFunction.build();
+            }
+        }
+        return null;
+    }
+
+    private Encapsulation buildSfEncapsulation() {
+        EncapsulationBuilder encapsulationBuilder = new EncapsulationBuilder();
+        GreBuilder greBuilder = new GreBuilder();
+        // TODO remove enhanced mode from encapsulation
+        greBuilder.setEnhanced(Gre.Enhanced.Divert);
+        encapsulationBuilder.setGre(greBuilder.build());
+        return encapsulationBuilder.build();
+    }
+
+    public void unregisterSfListener() {
+        sfListener.getRegistrationObject().close();
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManager.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManager.java
new file mode 100644 (file)
index 0000000..a7cc114
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.sfc.sfc_ios_xe.provider.listener.NodeListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus.ConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class NodeManager implements BindingAwareProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NodeManager.class);
+
+    private final NodeListener nodeListener;
+    private MountPointService mountService;
+    private final TopologyId topologyId = new TopologyId("topology-netconf");
+
+    // Data
+    private final Map<NodeId, Node> connectedNodes = new HashMap<>();
+    private final Map<NodeId, DataBroker> activeMountPoints = new HashMap<>();
+
+    public NodeManager(DataBroker dataBroker, BindingAwareBroker bindingAwareBroker) {
+        // Register provider
+        ProviderContext providerContext = bindingAwareBroker.registerProvider(this);
+        onSessionInitiated(providerContext);
+        // Node listener
+        nodeListener = new NodeListener(dataBroker, this);
+    }
+
+    public void updateNode(Node node) {
+        NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
+        Preconditions.checkNotNull(netconfNode);
+        // Check connection status
+        ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
+        NodeId netconfNodeId = node.getNodeId();
+        if (connectionStatus.equals(ConnectionStatus.Connected)) {
+            connectedNodes.put(netconfNodeId, node);
+            // Get mountpoint
+            InstanceIdentifier mountPointIid = getMountPointIid(netconfNodeId);
+            DataBroker dataBroker = getNetconfNodeDataBroker(mountPointIid);
+            if (dataBroker != null) {
+                activeMountPoints.put(netconfNodeId, dataBroker);
+            } else {
+                LOG.debug("Cannot obtain data broker for netconf node {}", netconfNodeId.getValue());
+                connectedNodes.remove(netconfNodeId);
+            }
+        }
+    }
+
+    public void removeNode(Node node) {
+        NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
+        Preconditions.checkNotNull(netconfNode);
+        ConnectionStatus connectionStatus = netconfNode.getConnectionStatus();
+        NodeId netconfNodeId = node.getNodeId();
+        switch (connectionStatus) {
+            case Connected: {
+                connectedNodes.remove(netconfNodeId);
+                activeMountPoints.remove(netconfNodeId);
+                LOG.info("Netconf node {} removed", netconfNodeId.getValue());
+            }
+        }
+    }
+
+    private DataBroker getNetconfNodeDataBroker(InstanceIdentifier mountPointIid) {
+        Optional<MountPoint> optionalObject = mountService.getMountPoint(mountPointIid);
+        MountPoint mountPoint;
+        if (optionalObject.isPresent()) {
+            mountPoint = optionalObject.get();
+            if (mountPoint != null) {
+                Optional<DataBroker> optionalDataBroker = mountPoint.getService(DataBroker.class);
+                if (optionalDataBroker.isPresent()) {
+                    return optionalDataBroker.get();
+                } else {
+                    LOG.debug("Cannot obtain data broker from mountpoint {}", mountPoint);
+                }
+            } else {
+                LOG.debug("Cannot obtain mountpoint with IID {}", mountPointIid);
+            }
+        }
+        return null;
+    }
+
+    private InstanceIdentifier getMountPointIid(NodeId nodeId) {
+        return InstanceIdentifier.builder(NetworkTopology.class)
+                .child(Topology.class, new TopologyKey(topologyId))
+                .child(Node.class, new NodeKey(nodeId)).build();
+    }
+
+    DataBroker getMountpointFromIpAddress(IpAddress ipAddress) {
+        for (Node node : connectedNodes.values()) {
+            if (ipAddress.equals(getNetconfNodeIp(node))) {
+                return activeMountPoints.get(node.getNodeId());
+            }
+        }
+        return null;
+    }
+
+    IpAddress getNetconfNodeIp(Node node) {
+        NetconfNode netconfNode = node.getAugmentation(NetconfNode.class);
+        Preconditions.checkNotNull(netconfNode);
+        return netconfNode.getHost().getIpAddress();
+    }
+
+    Map<NodeId, Node> getConnectedNodes() {
+        return connectedNodes;
+    }
+
+    Map<NodeId, DataBroker> getActiveMountPoints() {
+        return activeMountPoints;
+    }
+
+    public void unregisterNodeListener() {
+        nodeListener.getRegistrationObject().close();
+    }
+
+    @Override
+    public void onSessionInitiated(ProviderContext session) {
+        mountService = session.getSALService(MountPointService.class);
+        Preconditions.checkNotNull(mountService);
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPI.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPI.java
new file mode 100644 (file)
index 0000000..8d8410e
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Callable;
+
+public class IosXeDataStoreAPI implements Callable {
+
+    private final Logger LOG = LoggerFactory.getLogger(IosXeDataStoreAPI.class);
+
+    private final DataBroker mountpoint;
+    private final Object data;
+    private final Transaction currentTransaction;
+    private final LogicalDatastoreType datastoreType;
+
+    public enum Transaction {
+        WRITE_FUNCTION, READ_FUNCTION, DELETE_FUNCTION,
+        WRITE_LOCAL, READ_LOCAL, DELETE_LOCAL,
+        WRITE_REMOTE, READ_REMOTE, DELETE_REMOTE,
+        WRITE_PATH, READ_PATH, DELETE_PATH
+    }
+
+    public IosXeDataStoreAPI(DataBroker mountPoint, Object data, Transaction transaction,
+                             LogicalDatastoreType datastoreType) {
+        this.mountpoint = mountPoint;
+        this.data = data;
+        currentTransaction = transaction;
+        this.datastoreType = datastoreType;
+    }
+
+    @Override
+    public Object call() {
+        switch (currentTransaction) {
+            case WRITE_FUNCTION: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServiceFunction serviceFunction = (ServiceFunction) data;
+                    InstanceIdentifier<ServiceFunction> serviceFunctionIid = SfcIosXeUtils
+                            .createSfIid(serviceFunction.getKey());
+                    return writeMergeTransaction(serviceFunctionIid, serviceFunction);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServiceFunction", data);
+                }
+                break;
+            }
+            case READ_FUNCTION: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    SfName serviceFunction = (SfName) data;
+                    InstanceIdentifier<ServiceFunction> serviceFunctionIid = SfcIosXeUtils
+                            .createSfIid(new ServiceFunctionKey(serviceFunction.getValue()));
+                    return readTransaction(serviceFunctionIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServiceFunction", data);
+                }
+                break;
+            }
+            case DELETE_FUNCTION: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServiceFunctionKey serviceFunctionKey = (ServiceFunctionKey) data;
+                    InstanceIdentifier<ServiceFunction> serviceFunctionIid = SfcIosXeUtils
+                            .createSfIid(serviceFunctionKey);
+                    return deleteTransaction(serviceFunctionIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServiceFunction", data);
+                }
+                break;
+            }
+            case WRITE_LOCAL: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    Local localSff = (Local) data;
+                    InstanceIdentifier<Local> localIid = SfcIosXeUtils.createLocalSffIid();
+                    return writeMergeTransaction(localIid, localSff);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of Local", data);
+                }
+                break;
+            }
+            case READ_LOCAL: {
+                InstanceIdentifier<Local> localIid = SfcIosXeUtils.createLocalSffIid();
+                return readTransaction(localIid);
+            }
+            case DELETE_LOCAL: {
+                InstanceIdentifier<Local> localIid = SfcIosXeUtils.createLocalSffIid();
+                return deleteTransaction(localIid);
+            }
+            case WRITE_REMOTE: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServiceFfName remoteSff = (ServiceFfName) data;
+                    InstanceIdentifier<ServiceFfName> remoteIid = SfcIosXeUtils.createRemoteSffIid(remoteSff);
+                    return writeMergeTransaction(remoteIid, remoteSff);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServiceFfName", data);
+                }
+                break;
+            }
+            case DELETE_REMOTE: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServiceFfName remoteSff = (ServiceFfName) data;
+                    InstanceIdentifier<ServiceFfName> remoteIid = SfcIosXeUtils.createRemoteSffIid(remoteSff);
+                    return deleteTransaction(remoteIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServiceFfName", data);
+                }
+                break;
+            }
+            case READ_REMOTE: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    SffName remoteSffName = (SffName) data;
+                    InstanceIdentifier<ServiceFfName> remoteIid = SfcIosXeUtils.createRemoteSffIid(remoteSffName);
+                    return readTransaction(remoteIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of SffName", data);
+                }
+                break;
+            }
+            case WRITE_PATH: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServicePath path = (ServicePath) data;
+                    InstanceIdentifier<ServicePath> pathIid = SfcIosXeUtils.createServicePathIid(path
+                            .getKey());
+                    return writeMergeTransaction(pathIid, path);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServicePath", data);
+                }
+                break;
+            }
+            case READ_PATH: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServicePathKey pathKey = (ServicePathKey) data;
+                    InstanceIdentifier<ServicePath> pathIid = SfcIosXeUtils.createServicePathIid(pathKey);
+                    return readTransaction(pathIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServicePath", data);
+                }
+                break;
+            }
+            case DELETE_PATH: {
+                try {
+                    Preconditions.checkNotNull(data);
+                    ServicePathKey pathKey = (ServicePathKey) data;
+                    InstanceIdentifier<ServicePath> pathIid = SfcIosXeUtils.createServicePathIid(pathKey);
+                    return deleteTransaction(pathIid);
+                } catch (ClassCastException e) {
+                    LOG.error("Argument data {} is not an instance of ServicePathKey", data);
+                }
+                break;
+            }
+        }
+        return null;
+    }
+
+    private <U extends DataObject> boolean writeMergeTransaction(InstanceIdentifier<U> addIID, U data) {
+        WriteTransaction writeTransaction = mountpoint.newWriteOnlyTransaction();
+        try {
+            writeTransaction.merge(Preconditions.checkNotNull(datastoreType), addIID, data, true);
+            CheckedFuture<Void, TransactionCommitFailedException> submitFuture = writeTransaction.submit();
+            submitFuture.checkedGet();
+            return true;
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("Write transaction failed to {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private <U extends DataObject> boolean deleteTransaction(InstanceIdentifier<U> deleteIID) {
+        WriteTransaction writeTx = mountpoint.newWriteOnlyTransaction();
+        try {
+            writeTx.delete(Preconditions.checkNotNull(datastoreType), deleteIID);
+            CheckedFuture<Void, TransactionCommitFailedException> submitFuture = writeTx.submit();
+            submitFuture.checkedGet();
+            return true;
+        } catch (TransactionCommitFailedException e) {
+            LOG.error("Delete transaction failed to {}", e.getMessage());
+            return false;
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+            return false;
+        }
+    }
+
+    private <U extends DataObject> U readTransaction(InstanceIdentifier<U> readIID) {
+        ReadOnlyTransaction readTx = mountpoint.newReadOnlyTransaction();
+        try {
+            CheckedFuture<Optional<U>, ReadFailedException> submitFuture = readTx.read(Preconditions.checkNotNull(datastoreType), readIID);
+            Optional<U> optional = submitFuture.checkedGet();
+            if (optional != null && optional.isPresent()) {
+                return optional.get();
+            } else {
+                LOG.debug("Failed to read. {}", Thread.currentThread().getStackTrace()[1]);
+            }
+        } catch (ReadFailedException e) {
+            LOG.warn("Read transaction failed to {} ", e);
+        } catch (Exception e) {
+            LOG.error("Failed to .. {}", e.getMessage());
+        }
+        return null;
+    }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtils.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtils.java
new file mode 100644 (file)
index 0000000..30e8aa1
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.utils;
+
+import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.LocatorType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.Ip;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarderBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.LocalBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+public class SfcIosXeUtils {
+
+    private static final String REMOTE = "Remote forwarder: ";
+
+    /**
+     * Creates local service function forwarder {@link ServiceFunctionForwarder} with respective IP address. Local
+     * forwarder does not contain name, only IP address. Supports only IPv4
+     *
+     * @param ipAddress which will be set on service function forwarder
+     * @return service function forwarder object for ios-xe device which contains {@link Local} SFF. Null if parameter
+     * is not an IPv4 address
+     */
+    public static org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder createLocalForwarder(
+            IpAddress ipAddress) {
+        if (ipAddress != null && ipAddress.getIpv4Address() != null) {
+            // Ip address
+            IpBuilder ipBuilder = new IpBuilder();
+            ipBuilder.setAddress(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address(ipAddress
+                    .getIpv4Address().getValue()));
+            LocalBuilder localBuilder = new LocalBuilder();
+            localBuilder.setIp(ipBuilder.build());
+
+            ServiceFunctionForwarderBuilder localForwarderBuilder = new ServiceFunctionForwarderBuilder();
+            localForwarderBuilder.setLocal(localBuilder.build());
+            return localForwarderBuilder.build();
+        }
+        return null;
+    }
+
+    /**
+     * Creates remote service function forwarder (ios-xe SFC entity) {@link ServiceFfName}. Using name of the original
+     * entity, whole SFF configuration is read from ODL CONF data store. This configuration is used to
+     * build remote service function forwarder
+     *
+     * @param sffName name of the service function forwarder
+     * @return remote SFF (ios-xe SFC entity), null if SFF does not contain data plane locator with IP locator type
+     */
+    public static ServiceFfName createRemoteForwarder(SffName sffName) {
+        // Actually, local forwarder is without name. As a parameter, use SffName of appropriate sfc forwarder
+        ServiceFunctionForwarder sfcForwarder = SfcProviderServiceForwarderAPI.readServiceFunctionForwarder(sffName);
+        if (sfcForwarder == null) {
+            return null;
+        }
+        Ipv4Address sffIp = null;
+        for (SffDataPlaneLocator sffDpl : sfcForwarder.getSffDataPlaneLocator()) {
+            DataPlaneLocator dataPlaneLocator = sffDpl.getDataPlaneLocator();
+            LocatorType sffLocatorType = dataPlaneLocator.getLocatorType();
+            if (sffLocatorType instanceof Ip && ((Ip) sffLocatorType).getIp() != null) {
+                sffIp = ((Ip) sffLocatorType).getIp().getIpv4Address();
+            }
+        }
+        if (sffIp == null) {
+            return null;
+        }
+        IpBuilder ipBuilder = new IpBuilder();
+        ipBuilder.setAddress(new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address(sffIp.getValue()));
+        ServiceFfNameBuilder ffNameBuilder = new ServiceFfNameBuilder();
+        ffNameBuilder.setKey(new ServiceFfNameKey(sffName.getValue()))
+                .setName(sffName.getValue())
+                .setIp(ipBuilder.build())
+                .setDescription(REMOTE + sffName.getValue());
+        return ffNameBuilder.build();
+    }
+
+    /**
+     * From the set of data plane locators, choose the one with IP locator type
+     *
+     * @param dataPlaneLocators set of locators
+     * @return first DPL with IP locator type, null if no such locator is found
+     */
+    public static SfDataPlaneLocator getDplWithIpLocatorType(List<SfDataPlaneLocator> dataPlaneLocators) {
+        if (dataPlaneLocators == null) {
+            return null;
+        }
+        for (SfDataPlaneLocator sfDataPlaneLocator : dataPlaneLocators) {
+            LocatorType locatorType = sfDataPlaneLocator.getLocatorType();
+            if (locatorType instanceof Ip) {
+                return sfDataPlaneLocator;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Creates instance identifier for {@link Local} service function forwarder. This IID does not include any key.
+     * Every ios-xe device can contain just one local SFF, that means there is one Local SFF for mountpoint
+     *
+     * @return IID of the Local SFF
+     */
+    static InstanceIdentifier<Local> createLocalSffIid() {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(Local.class).build();
+    }
+
+    /**
+     * Creates instance identifier for {@link ServiceFfName} service function forwarder. Particular key is created using
+     * {@link ServiceFfName} object
+     *
+     * @return IID of the remote SFF
+     */
+    static InstanceIdentifier<ServiceFfName> createRemoteSffIid(@Nonnull ServiceFfName sffName) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(ServiceFfName.class, new ServiceFfNameKey(sffName.getName())).build();
+    }
+
+    /**
+     * Creates instance identifier for {@link ServiceFfName} service function forwarder. Particular key is created using
+     * {@link SffName} object
+     *
+     * @return IID of the remote SFF
+     */
+    static InstanceIdentifier<ServiceFfName> createRemoteSffIid(@Nonnull SffName sffName) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(ServiceFfName.class, new ServiceFfNameKey(sffName.getValue())).build();
+    }
+
+    /**
+     * Creates instance identifier for {@link ServiceFunction}
+     *
+     * @return IID of the SF
+     */
+    static InstanceIdentifier<ServiceFunction> createSfIid(@Nonnull ServiceFunctionKey key) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(ServiceFunction.class, key)
+                .build();
+    }
+
+    /**
+     * Creates instance identifier for {@link ServicePath}
+     *
+     * @return IID of the SP
+     */
+    static InstanceIdentifier<ServicePath> createServicePathIid(@Nonnull ServicePathKey key) {
+        return InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(ServicePath.class, key).build();
+    }
+
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/yang/gen/v1/http/tail/f/com/yang/common/rev150522/IpAddressAndPrefixLengthBuilder.java b/sfc-renderers/sfc-ios-xe-renderer/src/main/java/org/opendaylight/yang/gen/v1/http/tail/f/com/yang/common/rev150522/IpAddressAndPrefixLengthBuilder.java
new file mode 100644 (file)
index 0000000..c6dbaaf
--- /dev/null
@@ -0,0 +1,18 @@
+package org.opendaylight.yang.gen.v1.http.tail.f.com.yang.common.rev150522;
+
+
+/**
+ * The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.
+ * In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).
+ * <p>
+ * The reason behind putting it under src/main/java is:
+ * This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent
+ * loss of user code.
+ */
+public class IpAddressAndPrefixLengthBuilder {
+
+    public static IpAddressAndPrefixLength getDefaultInstance(java.lang.String defaultValue) {
+        throw new java.lang.UnsupportedOperationException("Not yet implemented");
+    }
+
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/resources/configuration/initial/sfc-ios-xe-initial.xml b/sfc-renderers/sfc-ios-xe-renderer/src/main/resources/configuration/initial/sfc-ios-xe-initial.xml
new file mode 100644 (file)
index 0000000..c28c3c1
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<snapshot>
+    <configuration>
+        <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                <module>
+                    <type xmlns:sfc-ios-xe="urn:opendaylight:params:xml:ns:yang:controller:config:sfc-ios-xe:impl">
+                        sfc-ios-xe:sfc-ios-xe-impl
+                    </type>
+                    <name>sfc-ios-xe-impl</name>
+
+                    <data-broker>
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+                        <name>binding-data-broker</name>
+                    </data-broker>
+
+                    <binding-registry>
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+                        <name>binding-osgi-broker</name>
+                    </binding-registry>
+                </module>
+            </modules>
+        </data>
+
+    </configuration>
+
+    <required-capabilities>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:sfc-provider:impl?module=sfc-provider-impl&amp;revision=2014-06-30</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:sfc-ios-xe:impl?module=sfc-ios-xe-impl&amp;revision=2014-10-20</capability>
+    </required-capabilities>
+
+</snapshot>
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/ned.yang b/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/ned.yang
new file mode 100644 (file)
index 0000000..680dc9f
--- /dev/null
@@ -0,0 +1,198 @@
+module ned {
+
+    prefix ios;
+    namespace
+        "urn:ios";
+
+    import tailf-common {
+        prefix tailf;
+    }
+
+    import ietf-inet-types {
+        prefix inet;
+        revision-date 2013-07-15;
+    }
+
+    description
+        "This file was adapted to be parsed by a yangtools";
+
+    revision
+        2016-03-08;
+
+    container native {
+        tailf:cli-drop-node-name;
+
+        grouping config-service-chain-grouping {
+            leaf description {
+                tailf:info "Service function forwarder description";
+                    description "Service function forwarder description";
+                    tailf:cli-multi-value;
+                    type string {
+                        tailf:info "LINE;;Up to 256 characters describing this "
+                            + "service function forwarder";
+                        length "1..256";
+                    }
+                }
+                container ip {
+                    tailf:info "IP address for Service Function Forwarder";
+                    description "IP address for Service Function Forwarder";
+                    leaf address {
+                        tailf:info "Set IPv4 address";
+                        description "Set IPv4 address";
+                        type inet:ipv4-address {
+                            tailf:info "A.B.C.D;;IP address of Service Function Forwarder";
+                        }
+                    }
+                }
+            }
+
+            container service-chain {
+                tailf:info "Service Chain mode";
+                description "Service Chain mode";
+                list service-function {
+                    tailf:info "Service function details";
+                    description "Service function details";
+                    tailf:cli-mode-name "config-service-chain-sf";
+                    key "name";
+                    leaf name {
+                        tailf:info "WORD;;Service function name";
+                        description "WORD;;Service function name";
+                        type string;
+                    }
+                    container config-service-chain-sf-mode {
+                        tailf:cli-drop-node-name;
+                        leaf description {
+                            tailf:info "Service function description";
+                            description "Service function description";
+                            tailf:cli-multi-value;
+                            tailf:cli-full-command;
+                            type string {
+                                tailf:info "LINE;;Up to 256 characters describing this "
+                                    +"service function";
+                                length "1..256";
+                            }
+                        }
+                        container encapsulation {
+                            tailf:info "Service node encapsulation";
+                            description "Service node encapsulation";
+                            container gre {
+                                tailf:info "Service node encapsulation type";
+                                description "Service node encapsulation type";
+                                leaf enhanced {
+                                    tailf:info "Add enhanced NSH TLV information";
+                                    description "Add enhanced NSH TLV information";
+                                    type enumeration {
+                                        enum copy {
+                                            tailf:info "Send a copy of the packet";
+                                        }
+                                        enum divert {
+                                            tailf:info "Divert the packet (default)";
+                                        }
+                                    }
+                                }
+                            }
+                            leaf none {
+                                tailf:info "Service node encapsulation type";
+                                description "Service node encapsulation type";
+                                type empty;
+                            }
+                        }
+                        container ip {
+                            tailf:info "Service node";
+                            description "Service node";
+                            leaf address {
+                                type inet:ipv4-address {
+                                    tailf:info "A.B.C.D;;Service node";
+                                }
+                            }
+                        }
+                    }
+                }
+                container service-function-forwarder {
+                    tailf:info "Service function forwarder details";
+                    description "Service function forwarder details";
+                    list service-ff-name {
+                        tailf:cli-drop-node-name;
+                        tailf:cli-mode-name "config-service-chain-sff";
+                        key "name";
+                        leaf name {
+                            tailf:info "WORD;;Service function forwarder name";
+                            description "WORD;;Service function forwarder name";
+                            type string;
+                        }
+                        uses config-service-chain-grouping;
+                    }
+                    container local {
+                        tailf:info "Local service function forwarder";
+                        description "Local service function forwarder";
+                        tailf:cli-add-mode;
+                        tailf:cli-mode-name "config-service-chain-sff";
+                        presence true;
+                        uses config-service-chain-grouping;
+                    }
+                }
+                list service-path {
+                    tailf:info "Service Path Entries";
+                    description "Service Path Entries";
+                    tailf:cli-mode-name "config-service-chain-path";
+                    key "service-path-id";
+                    leaf service-path-id {
+                        type uint32 {
+                            range "0..16777215";
+                            tailf:info "<0-16777215>;;Service Path ID";
+                        }
+                    }
+                    container config-service-chain-path-mode {
+                        tailf:cli-drop-node-name;
+                        leaf description {
+                            tailf:info "Path Description";
+                            description "Path Description";
+                            tailf:cli-multi-value;
+                            tailf:cli-full-command;
+                            type string {
+                                tailf:info "LINE;;Up to 256 characters describing this "
+                                    +"service path";
+                                length "1..256";
+                            }
+                        }
+                        container service-index {
+                            tailf:info "Service Index";
+                            description "Service Index";
+                            list services {
+                                tailf:cli-drop-node-name;
+                                tailf:cli-suppress-mode;
+                                key "service-index-id";
+                                leaf service-index-id {
+                                    type uint8 {
+                                    range "2..255";
+                                    tailf:info "<2-255>;;Service Index ID";
+                                }
+                            }
+                            choice service-type-choice {
+                                leaf service-function {
+                                    tailf:info "Service Function name";
+                                    description "Service Function name";
+                                    type string {
+                                        tailf:info "WORD;;Service Function name";
+                                    }
+                                }
+                                leaf service-function-forwarder {
+                                    tailf:info "Service Function Forwarder name";
+                                    description "Service Function Forwarder name";
+                                    type string {
+                                        tailf:info "WORD;;Service Function Forwarder name";
+                                    }
+                                }
+                                leaf terminate {
+                                    tailf:info "Terminate(Proxy)";
+                                    description "Terminate(Proxy)";
+                                    type empty;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-cli-extensions.yang b/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-cli-extensions.yang
new file mode 100644 (file)
index 0000000..764f8c0
--- /dev/null
@@ -0,0 +1,2611 @@
+submodule tailf-cli-extensions {
+
+  belongs-to tailf-common {
+    prefix tailf;
+  }
+
+  include tailf-meta-extensions {
+    revision-date 2013-11-07;
+  }
+
+  organization "Tail-f Systems";
+
+  description
+    "This module defines all Tail-f YANG extensions statements
+     related to CLI customization.
+
+     See also the 'display-' statements and the 'alt-name' statement
+     in tailf-common.yang.";
+
+  revision 2015-03-19;
+
+  extension cli-show-no {
+    tailf:use-in "leaf";
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    description
+      "Specifies that an optional leaf node or presence container
+      should be displayed as 'no <name>' when it does not exist.
+      For example, if a leaf 'shutdown' has this property and
+      does not exist, 'no shutdown' is displayed.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-disallow-value {
+    argument value {
+      tailf:arg-type {
+         type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    description
+      "Specifies that a pattern for invalid values.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-boolean-no {
+    tailf:use-in "typedef";
+    tailf:use-in "leaf";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    tailf:substatement "tailf:cli-reversed";
+    description
+      "Specifies that a leaf of type boolean should be displayed as
+       '<leafname>' if set to true, and 'no <leafname>' if set to
+       false.
+
+       Cannot be used in conjunction with tailf:cli-hide-in-submode
+       or tailf:cli-compact-syntax.
+
+       Used in I- and C-style CLIs.";
+  }
+
+  extension cli-reversed {
+    tailf:use-in "tailf:cli-boolean-no";
+    description
+      "Specified that true should be displayed as 'no <name>' and
+       false as 'name'.
+
+       Used in I- and C-style CLIs.";
+  }
+
+  extension cli-autowizard {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the autowizard should include this leaf even
+       if the leaf is optional.
+
+       One use case is when implementing pre-configuration of devices.
+       A config false node can be defined for showing if the
+       configuration is active or not (preconfigured).
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-show-config {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    description
+      "Specifies that the node will be included when doing a 'show
+      running-configuration', even if it is a non-config node.
+
+      Used in I- and C-style CLIs.";
+  }
+
+
+  extension cli-display-empty-config {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the node will be included when doing a 'show
+      stats', even if it is a non-config node, provided
+      that the list contains at least one non-config node.
+
+      Used in J-style CLI.";
+  }
+
+  extension cli-mode-name {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a custom mode name, instead of the default which is the
+      name of the list or container node.
+
+      Can be used in config nodes only.  If used in a container, the
+      container must also have a tailf:cli-add-mode statement, and if
+      used in a list, the list must not also have a
+      tailf:cli-suppress-mode statement.
+
+      Variables for the list keys in the current mode are available.
+      For examples, 'config-foo-xx$(name)' (privided the key leaf
+      is called 'name').
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-show-order-taglist {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a custom display order for nodes with the
+       tailf:cli-show-order-tag attribute. Nodes will be displayed
+       in the order indicated in the list. Nodes without a tag will
+       be displayed after all nodes with a tag have been displayed.
+
+       The scope of a taglist is until a new taglist is encountered.
+
+       Used in I- and C-style CLIs.";
+  }
+
+  extension cli-show-order-tag {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a custom display order for nodes with the
+       tailf:cli-show-order-tag attribute. Nodes will be displayed
+       in the order indicated by a cli-show-order-taglist attribute in
+       a parent node.
+
+       The scope of a tag reaches until a new taglist is encountered.
+
+       Used in I- and C-style CLIs.";
+  }
+
+  extension cli-mode-name-actionpoint {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "refine";
+    description
+      "Specifies that a custom function will be invoked to find out the mode
+      name, instead of using the default with is the name of the list
+      or container node.
+
+      The argument is the name of an actionpoint, which must be
+      implemented by custom code.  In the actionpoint, the command()
+      callback function will be invoked, and it must return a string
+      with the mode name.  See confd_lib_dp(3) for details.
+
+      Can be used in config nodes only.  If used in a container, the
+      container must also have a tailf:cli-add-mode statement, and if
+      used in a list, the list must not also have a
+      tailf:cli-suppress-mode statement.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-add-mode {
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Creates a mode of the container.
+
+      Can be used in config nodes only.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-flatten-container {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Allows the CLI to exit the container and continue to input
+       from the parent container when all leaves in the current
+       container has been set.
+
+      Can be used in config nodes only.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-mode {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to not make a mode of the list node.
+
+      Can be used in config nodes only.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-delete-when-empty {
+    tailf:use-in "list";
+    tailf:use-in "container";
+    description
+      "Instructs the CLI engine to delete the list when the last list
+       instance is deleted'. Requires that cli-suppress-mode is set.
+
+       The behavior is recursive. If all optional leafs in a list
+       instance are deleted the list instance itself is deleted. If
+       that list instance happens to be the last list instance in a
+       list it is also deleted. And so on. Used in I- and C-style
+       CLIs.";
+  }
+
+  extension cli-remove-before-change {
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to generate a no-commnd before
+       modifying an existing instance. It only applies when
+       generating diffs, eg 'show configuration' in C-style.";
+  }
+
+  extension cli-no-value-on-delete {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "When displaying the deleted version of this leaf do not
+       include the old value.
+
+       Applies to C-style";
+  }
+
+  extension cli-no-name-on-delete {
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "When displaying the deleted version of this element do not
+       include the name.
+
+       Applies to C-style";
+  }
+
+  extension cli-embed-no-on-delete {
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Embed no in front of the element name insead of at the
+       beginning of the line.
+
+       Applies to C-style";
+  }
+
+  extension cli-recursive-delete {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "When generating configuration diffs delete all contents
+       of a container or list before deleting the node.
+
+       Applies to C-style";
+  }
+
+
+  extension cli-diff-dependency {
+    argument path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:xpath-root";
+    tailf:substatement "tailf:cli-trigger-on-set";
+    tailf:substatement "tailf:cli-trigger-on-delete";
+    tailf:substatement "tailf:cli-trigger-on-all";
+    tailf:occurence "*";
+    description
+      "Tells the 'show configuration' command, and the diff generator
+       that this node depends on another node. When removing the node
+       with this declaration, it should be removed before the node
+       it depends on is removed, ie the declaration controlls the ordering
+       of the commands in the 'show configuration' output.
+
+       Applies to C-style";
+  }
+
+  extension cli-trigger-on-set {
+    tailf:use-in "tailf:cli-diff-dependency";
+    description
+      'Specify that the dependency should trigger on set/modify of
+       the target path, but deletion of the target will trigger the
+       current node to be placed in front of the target.
+
+       The annotation can be used to get the diff behavior where
+       one leaf is first deleted before the other leaf is set.
+       For example, having the data model below:
+
+        container X {
+          leaf A {
+            tailf:cli-diff-dependency "../B" {
+              tailf:cli-trigger-on-set;
+            }
+            type empty;
+          }
+          leaf B {
+            tailf:cli-diff-dependency "../A"  {
+              tailf:cli-trigger-on-set;
+            }
+            type empty;
+          }
+        }
+
+       produces the following diffs when setting one leaf
+       and deleting the other
+
+         no X A
+         X B
+
+       and
+
+         no X B
+         X A
+
+       this can also be done with list instances, for example
+
+         list a {
+           key id;
+
+           leaf id {
+             tailf:cli-diff-dependency "/c[id=current()/../id]" {
+               tailf:cli-trigger-on-set;
+             }
+             type string;
+           }
+         }
+
+         list c {
+           key id;
+           leaf id {
+             tailf:cli-diff-dependency "/a[id=current()/../id]" {
+               tailf:cli-trigger-on-set;
+             }
+             type string;
+           }
+         }
+
+       we get
+
+         no a foo
+         c foo
+         !
+
+       and
+
+         no c foo
+         a foo
+         !
+
+       In the above case if we have the same id in list "a" and "c"
+       and we delete the instance in one list, and add it in the other,
+       then the deletion will always preceed the create.
+      ';
+  }
+
+  extension cli-trigger-on-delete {
+    tailf:use-in "tailf:cli-diff-dependency";
+    description
+      "This annotation can be used togeter with tailf:cli-trigger-on-set
+       to also get the behavior that when deleting the target display
+       changes to this node first. For exmaple:
+
+        container settings {
+          tailf:cli-add-mode;
+
+          leaf opmode {
+            tailf:cli-no-value-on-delete;
+
+            type enumeration {
+              enum nat;
+              enum transparent;
+            }
+          }
+
+          leaf manageip {
+            when \"../opmode = 'transparent'\";
+            mandatory true;
+            tailf:cli-no-value-on-delete;
+            tailf:cli-diff-dependency '../opmode' {
+              tailf:cli-trigger-on-set;
+              tailf:cli-trigger-on-delete;
+            }
+
+            type string;
+          }
+        }
+
+      What we are trying to achieve here is that if manageip is
+      deleted, it should be displayed before opmode, but if we
+      configure both opmode and manageip, we should display opmode
+      first, ie get the diffs:
+
+       settings
+        opmode   transparent
+        manageip 1.1.1.1
+       !
+
+      and
+
+       settings
+        no manageip
+        opmode nat
+       !
+
+      and
+
+       settings
+        no manageip
+        no opmode
+       !
+
+      The cli-trigger-on-set annotation will cause the 'no manageip'
+      command to be displayed before setting opmode. The
+      tailf:cli-trigger-on-delete will cause 'no manageip' to be
+      placed before 'no opmode' when both are deleted.
+
+      In the first diff where both are created, opmode will come first
+      due to the diff-dependency setting, regardless of the
+      cli-trigger-on-delete and cli-trigger-on-set.
+      ";
+
+  }
+
+
+  extension cli-trigger-on-all {
+    tailf:use-in "tailf:cli-diff-dependency";
+    description
+      "Specify that the dependency should always trigger. It is the
+       same as placing one element before another in the data model.
+       For example, given the data model:
+
+       container X {
+         leaf A {
+           tailf:cli-diff-dependency '../B' {
+             tailf:cli-trigger-on-all;
+           }
+           type empty;
+         }
+         leaf B {
+           type empty;
+         }
+       }
+
+       We get the diffs
+
+         X B
+         X A
+
+       and
+
+         no X B
+         no X A
+      ";
+  }
+
+
+  extension cli-ignore-modified {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Tells the cdb_cli_diff_iterate system call to not generate
+       a CLI string when this container is modified. The string will
+       instead be generated for the modified sub-element.
+
+       Applies to C-style and I-style";
+  }
+
+  extension cli-show-long-obu-diffs {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to not generate 'insert' comments
+       when displaying configuration changes of ordered-by user
+       lists, but instead explicitly remove old instances with 'no'
+       and then add the instances following a newly inserted instance.
+       Should not be used together with tailf:cli-show-obu-comments";
+  }
+
+  extension cli-show-obu-comments {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Enforces the CLI engine to generate 'insert' comments
+       when displaying configuration changes of ordered-by user
+       lists. Should not be used together with tailf:cli-show-long-obu-diffs";
+  }
+
+  extension cli-allow-join-with-key {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-display-joined";
+    description
+      "Indicates that the list name may be written together
+      with the first key, without requiring a whitespace
+      in between, ie allowing both
+            interface ethernet1/1
+      and
+            interface ethernet 1/1
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-allow-join-with-value {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-display-joined";
+    description
+      "Indicates that the leaf name may be written together
+      with the value, without requiring a whitespace
+      in between, ie allowing both
+            interface ethernet1/1
+      and
+            interface ethernet 1/1
+
+      Used in I- and C-style CLIs.";
+  }
+
+
+  extension cli-display-joined {
+    tailf:use-in "tailf:cli-allow-join-with-key";
+    tailf:use-in "tailf:cli-allow-join-with-value";
+    description
+      "Specifies that the joined version should be used when displaying
+       the configuration in C- and I- mode.";
+  }
+
+  extension cli-exit-command {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    tailf:substatement "tailf:info";
+    description
+      "Tells the CLI to add an explicit exit-from-submode command.
+       The tailf:info substatement can be used for adding a custom
+       info text for the command.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-explicit-exit {
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    description
+      "Tells the CLI to add an explicit exit command when displaying
+       the configuration. It will not be added if cli-exit-command
+       is defined as well. The annotation is inherited by all
+       sub-modes.
+
+      Used in I- and C-style CLIs.";
+  }
+
+
+  extension cli-key-format {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "The format string is used when parsing a key value and when
+      generating a key value for an existing configuration. The key
+      items are numbered from 1-N and the format string should
+      indicate how they are related by using $(X) (where X is the
+      key number). For example:
+
+      tailf:cli-key-format '$(1)-$(2)' means that the first key
+      item is concatenated with the second key item by a '-'.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-key-sort {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to not sort the keys in alphabetical order
+      when presenting them to the user during TAB completion.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-table {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to not print the list as a table in
+      the 'show' command.
+
+      Can be used in non-config nodes only.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-validation-warning-prompt {
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to not prompt the user whether to proceed
+      or not if a warning is generated for this node.
+
+      Used in I- and C-style CLIs.";
+  }
+
+
+  extension cli-suppress-key-abbreviation {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Key values cannot be abbreviated.  The user must always give
+      complete values for keys.
+
+      In the J-style CLI this is relevant when using the commands
+      'delete' and 'edit'.
+
+      In the I- and C-style CLIs this is relevant when using the
+      commands 'no', 'show configuration' and for commands to enter
+      submodes.
+
+      See also /confdConfig/cli/allowAbbrevKeys in confd.conf(5).";
+  }
+
+  extension cli-allow-key-abbreviation {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Key values can be abbreviated.
+
+      In the J-style CLI this is relevant when using the commands
+      'delete' and 'edit'.
+
+      In the I- and C-style CLIs this is relevant when using the
+      commands 'no', 'show configuration' and for commands to enter
+      submodes.
+
+      See also /confdConfig/cli/allowAbbrevKeys in confd.conf(5).";
+  }
+
+  extension cli-table-legend {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be printed before all list entries are
+      printed.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-table-footer {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be printed after all list entries are
+      printed.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-completion-actionpoint {
+    argument value {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "leaf-list";
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-completion-id";
+    description
+      "Specifies that completion for the leaf values is done through a
+      callback function.
+
+      The argument is the name of an actionpoint, which must be
+      implemented by custom code.  In the actionpoint, the completion()
+      callback function will be invoked.  See confd_lib_dp(3) for details.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-completion-id {
+    argument value {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "tailf:cli-completion-actionpoint";
+    tailf:use-in "tailf:cli-custom-range-actionpoint";
+    description
+      "Specifies a string which is passed to the callback when invoked.
+      This makes it possible to use the same callback at several
+      locations and still keep track of which point it is invoked
+      from.";
+  }
+
+  extension cli-allow-caching {
+    tailf:use-in "tailf:cli-custom-range-actionpoint";
+    tailf:use-in "tailf:cli-custom-range-enumerator";
+    description
+      "Allow caching of the evaluation results between different parent paths.";
+  }
+
+  extension cli-multi-line-prompt {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Tells the CLI to automatically enter multi-line mode when prompting
+       the user for a value to this leaf.
+
+       Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-multi-word-key {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-max-words";
+    description
+      "Specifies that the key should allow multiple tokens for the
+       value. Proper type restrictions needs to be used to limit
+       the range of the leaf value.
+
+      Can be used in key leafs only.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-max-words {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "tailf:cli-multi-word-key";
+    tailf:use-in "tailf:cli-multi-word";
+    tailf:use-in "tailf:cli-multi-value";
+    description
+      "Specifies the maximum number of allowed words for the key or value.";
+  }
+
+  extension cli-allow-range {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Means that the non-integer key should allow range expressions.
+
+      Can be used in key leafs only.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-range-delimiters {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Allows for custom delimiters to be defined for range expressions.
+      By default only / is considered a delimiter, ie when processing
+      a key like 1/2/3 then each of 1, 2 and 3 will be matched separately
+      agains range expressions, ie given the expression 1-3/5-6/7,8
+      1 will be matched with 1-3, 2 with 5-6, and 3 with 7,8. If, for
+      example, the delimiters value is set to '/.' then both '/' and
+      '.' will be considered delimiters and an key such as 1/2/3.4 will
+      consist of the enteties 1,2,3,4, all matched separately.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+
+  extension cli-suppress-range {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Means that the integer key should not allow range expressions.
+
+      Can be used in key leafs only.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-custom-range {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-range-type" {
+      tailf:occurence "1";
+    }
+    description
+      "Specifies that the key should support ranges.  A type matching the
+      range expression must be supplied.
+
+      Can be used in key leafs only.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-custom-range-actionpoint {
+    argument value {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-completion-id";
+    tailf:substatement "tailf:cli-allow-caching";
+    description
+      "Specifies that the list supports range expressions and that a custom
+      function will be invoked to determine if an instance belong in
+      the range or not. At least one key element needs a
+      cli-custom-range statement.
+
+      The argument is the name of an actionpoint, which must be
+      implemented by custom code.  In the actionpoint, the
+      completion() callback function will be invoked.  See
+      confd_lib_dp(3) for details.
+
+      When a range expression value which matches the type is given in
+      the CLI, the CLI engine will invoke the callback with each
+      existing list entry instance.  If the callback returns CONFD_OK,
+      it matches the range expression, and if it returns CONFD_ERR, it
+      doesn't match.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-custom-range-enumerator {
+    argument value {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-completion-id";
+    tailf:substatement "tailf:cli-allow-caching";
+    description
+      "Specifies a callback to invoke to get an array of
+      instances matching a regular expression. This is used
+      when instances should be allowed to be created using
+      a range expression in set.
+
+      The callback is not used for delete or show operations.
+
+      The callback is allowed to return a superset of all matching
+      instances since the instances will be filtered using the
+      range expression afterwards.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-range-type {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:cli-custom-range";
+    description
+      "This statement contains the name of a derived type, possibly
+      with a prefix.  If no prefix is given, the type must be defined in
+      the local module.  For example:
+
+        cli-range-type p:my-range-type;
+
+      All range expressions must match this type, and a valid key
+      value must not match this type.";
+  }
+
+  extension cli-allow-wildcard {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Means that the list allows wildcard expressions in the 'show' pattern.
+
+      See also /confdConfig/cli/allowWildcard in confd.conf(5).
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-wildcard {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Means that the list does not allow wildcard expressions in the 'show'
+      pattern.
+
+      See also /confdConfig/cli/allowWildcard in confd.conf(5).
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-configure-mode {
+    tailf:use-in "tailf:action";
+    tailf:use-in "rpc";
+    description
+      "An action or rpc with this attribute will be available in
+       configure mode, but not in operational mode.
+
+       The default is that the action or rpc is available in both
+       configure and operational mode.
+
+       Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-operational-mode {
+    tailf:use-in "tailf:action";
+    tailf:use-in "rpc";
+    description
+      "An action or rpc with this attribute will be available in
+       operational mode, but not in configure mode.
+
+       The default is that the action or rpc is available in both
+       configure and operational mode.
+
+       Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-mount-point {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:action";
+    tailf:use-in "rpc";
+    description
+      "By default actions are mounted under the 'request'
+       command in the J-style CLI and at the top-level in
+       the I- and C-style CLIs. This annotation allowes
+       the action to be mounted under other top level commands";
+  }
+
+  extension cli-batch-confirm-default {
+    argument name {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:confirm-text";
+    description
+      "Specifies if the default is to proceed or abort the action during batch
+      processing in the CLI (e.g. non-interactive mode) when a confirm-text is
+      set. If this value is not specified, the default value may instead be
+      provdied by tailf:confirm-default or by the ConfD global default if
+      specified in a clispec(5).";
+  }
+
+  extension cli-delayed-auto-commit {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Enables transactions while in a specific submode (or submode of that
+      mode).  The modifications performed in that mode will not take effect
+      until the user exits that submode.
+
+      Can be used in config nodes only.  If used in a container, the
+      container must also have a tailf:cli-add-mode statement, and if
+      used in a list, the list must not also have a
+      tailf:cli-suppress-mode statement.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-preformatted {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Suppresses quoting of non-config elements when displaying them.
+      Newlines will be preserved in strings etc.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-disabled-info {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies an info string that will be used as a descriptive text for the
+      value 'disable' (false) of boolean-typed leafs when the confd.conf(5)
+      setting /confdConfig/cli/useShortEnabled is set to 'true'.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-shortenabled {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Suppresses the confd.conf(5) setting /confdConfig/cli/useShortEnabled.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-trim-default {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Do not display value if it is same as default.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-expose-key-name {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Force the user to enter the name of the key and display the
+      key name when displaying the running-configuration.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+
+  extension cli-enforce-table {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Forces the generation of a table for a list element node regardless of
+      whether the table will be too wide or not.  This applies to the
+      tables generated by the auto-rendred show commands for non-config data.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-drop-node-name {
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the name of a node is not present in the CLI.
+
+      If tailf:cli-drop-node-name is given on a child to a list node,
+      we recommend that you also use tailf:cli-suppress-mode on that
+      list node, otherwise the CLI will be very confusing.
+
+      For example, consider this data model, from the tailf-aaa module:
+
+      list alias {
+        key name;
+        leaf name {
+          type string;
+        }
+        leaf expansion {
+          type string;
+          mandatory true;
+          tailf:cli-drop-node-name;
+        }
+      }
+
+      If you type 'alias foo' in the CLI, you would end up in the
+      'alias' submode.  But since the expansion is dropped, you would
+      end up specifying the expansion value without typing any command.
+
+      If, on the other hand, the 'alias' list had a
+      tailf:cli-suppress-mode statement, you would set an expansion
+      'bar' by typing 'alias foo bar'.
+
+      tailf:cli-drop-node-name cannot be used inside tailf:action.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-no-keyword {
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the name of a node is not present in the CLI.
+
+
+       Note that is must be used with some care, just
+       like tailf:cli-drop-node-name. The resulting data model must still
+       be possible to parse deterministically.
+       For example, consider the data model
+
+       container interfaces {
+          list traffic {
+              tailf:cli-no-keyword;
+              key id;
+              leaf id { type string; }
+              leaf mtu { type uint16; }
+          }
+          list management {
+              tailf:cli-no-keyword;
+              key id;
+              leaf id { type string; }
+              leaf mtu { type uint16; }
+          }
+       }
+
+       In this case it is impossible to determine if the config
+
+       interfaces {
+          eth0 {
+             mtu 1400;
+           }
+       }
+
+       Means that there should be an traffic interface instance named
+       'eth0' or a management interface instance maned 'eth0'. If, on
+       the other hand, a restriction on the type was used, for example
+
+       container interfaces {
+          list traffic {
+              tailf:cli-no-keyword;
+              key id;
+              leaf id { type string; pattern 'eth.*'; }
+              leaf mtu { type uint16; }
+          }
+          list management {
+              tailf:cli-no-keyword;
+              key id;
+              leaf id { type string; pattern 'lo.*';}
+              leaf mtu { type uint16; }
+          }
+       }
+
+       then the problem would disappear.
+
+      Used in the J-style CLIs.";
+  }
+
+  extension cli-compact-syntax {
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Instructs the CLI engine to use the compact representation for this
+      node in the 'show running-configuration' command.  The compact
+      representation means that all leaf elements are shown on a
+      single line.
+
+      Cannot be used in conjunction with tailf:cli-boolean-no.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-compact-stats {
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-wrap";
+    tailf:substatement "tailf:cli-width";
+    tailf:substatement "tailf:cli-delimiter";
+    tailf:substatement "tailf:cli-prettify";
+    tailf:substatement "tailf:cli-spacer";
+    description
+      "Instructs the CLI engine to use the compact representation for this
+      node.  The compact representation means that all leaf elements
+      are shown on a single line.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-wrap {
+    tailf:use-in "tailf:cli-compact-stats";
+    description
+      "If present, the line will be wrapped at screen width.";
+  }
+
+  extension cli-width {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "tailf:cli-compact-stats";
+    description
+      "Specifies a fixed terminal width to use before wrapping line. It is
+      only used when tailf:cli-wrap is present.  If a width is not
+      specified the line is wrapped when the terminal width is
+      reached.";
+  }
+
+  extension cli-delimiter {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:cli-compact-stats";
+    description
+      "Specifies a string to print between the leaf name and its value
+      when displaying leaf values.";
+  }
+
+  extension cli-prettify {
+    tailf:use-in "tailf:cli-compact-stats";
+    description
+      "If present, dashes (-) and underscores (_) in leaf names are replaced
+      with spaces.";
+  }
+
+  extension cli-spacer {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:cli-compact-stats";
+    description
+      "Specifies a string to print between the nodes.";
+  }
+
+
+  extension cli-column-stats {
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    description
+      "Display leafs in the container as columns, i.e., do not repeat
+      the name of the container on each line, but instead indent each
+      leaf under the container.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-column-width {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Set a fixed width for the column in the auto-rendered tables.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-min-column-width {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Set a minimum width for the column in the auto-rendered tables.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-column-align {
+    argument value {
+      tailf:arg-type {
+        type enumeration {
+          enum left;
+          enum center;
+          enum right;
+        }
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies the alignment of the data in the column in the
+      auto-rendered tables.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-list-syntax {
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-multi-word";
+    description
+      "Specifies that each entry in a leaf-list should be displayed as
+       a separate element.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-multi-word {
+    tailf:use-in "tailf:cli-list-syntax";
+    tailf:substatement "tailf:cli-max-words";
+    description
+      "Specifies that a multi-word value may be entered without quotes.";
+  }
+
+  extension cli-flat-list-syntax {
+    tailf:use-in "leaf-list";
+    tailf:substatement "tailf:cli-replace-all";
+    tailf:use-in "refine";
+    description
+      "Specifies that elements in a leaf-list should be entered without
+      surrounding brackets. Also, multiple elements can be added to a list
+      or deleted from a list.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-replace-all {
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:cli-flat-list-syntax";
+    tailf:use-in "refine";
+    description
+      "Specifies that the new leaf-list value(s) should replace the old,
+      as opposed to be added to the old leaf-list.";
+  }
+
+  extension cli-range-list-syntax {
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "refine";
+    description
+      "Specifies that elements in a leaf-list or a list should be entered
+      without surrounding brackets and presented as ranges.  The
+      element in the list should be separated by a comma.  For
+      example:
+
+        vlan 1,3,10-20,30,32,300-310
+
+      When this statement is used for lists, the list must have a
+      single key.  The elements are be presented as ranges as above.
+
+      The type of the list key, or the leaf-list, must be integer based.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-incomplete-command {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that an auto-rendered command should be considered
+      incomplete.  Can be used to prevent <cr> from appearing in
+      the completion list for optional internal nodes, for example, or
+      to ensure that the user enters all leaf values in a container
+      (if used in combination with cli-sequence-commands).
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-full-command {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that an auto-rendered command should be considered complete,
+      ie, no additional leaves or containers can be entered on the same
+      command line.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-sequence-commands {
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-reset-siblings";
+    tailf:substatement "tailf:cli-reset-all-siblings";
+    description
+      "Specifies that an auto-rendered command should only accept arguments
+      in the same order as they are specified in the YANG model.
+      This, in combination with tailf:cli-drop-node-name, can be used
+      to create CLI commands for setting multiple leafs in a container
+      without having to specify the leaf names.
+
+      In almost all cases this annotation should be accompanied by the
+      tailf:cli-compact-syntax annotation. Otherwise the output from
+      'show running-config' will not be correct, and the sequence
+      'save xx' 'load override xx' will not work.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-reset-siblings {
+    tailf:use-in "tailf:cli-sequence-commands";
+    description
+      "Specifies that all sibling leaves in the sequence should be reset
+       whenever the first leaf in the sequence is set.";
+  }
+
+  extension cli-reset-all-siblings {
+    tailf:use-in "tailf:cli-sequence-commands";
+    description
+      "Specifies that all sibling leaves in the container should be reset
+       whenever the first leaf in the sequence is set.";
+  }
+
+  extension cli-reset-container {
+    tailf:use-in "leaf";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that all sibling leaves in the container should be
+       reset when this element is set.
+
+       When used on a container its content is cleared when set.";
+  }
+
+  extension cli-display-separated {
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Tells CLI engine to display this container as a separate
+       line item even when it has children. Only applies to
+       presence containers.
+
+       Applicable for optional containers in the C- and I- style CLIs.";
+  }
+
+  extension cli-delete-container-on-delete {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the parent container should be deleted when
+.      this leaf is deleted.";
+  }
+
+  extension cli-break-sequence-commands {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that previous cli-sequence-command declaration should
+      stop at this point. Only applicable when a cli-sequence-command
+      declaration has been used in the parent container.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-strict-leafref {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the leaf should only be allowed to be assigned
+       references to existing instances when the command is executed.
+       Without this annotation the requirement is that the instance
+       exists on commit time.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-optional-in-sequence {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that this element is optional in the sequence. If it
+       is set it must be set in the right sequence but may be skipped.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-incomplete-show-path {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-min-keys";
+    description
+      "Specifies that a path to the show command is considered incomplete,
+      i.e., it needs more elements added to the path.  It can also be used
+      to specify a minimum number of keys to be given for lists.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-min-keys {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "tailf:cli-incomplete-show-path";
+    description
+      "Specifies the minimum number of required keys for the show command.";
+  }
+
+  extension cli-hide-in-submode {
+    tailf:use-in "leaf";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Hide leaf when submode has been entered. Mostly useful when
+       leaf has to be entered in order to enter a submode. Also works
+       for flattened containers.
+
+       Cannot be used in conjunction with tailf:cli-boolean-no.
+
+       Used in I- and C-style CLIs.";
+  }
+
+  extension cli-expose-ns-prefix {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "When used force the CLI to display namespace prefix of all children.";
+  }
+
+  extension cli-prefix-key {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:use-in "leaf-list";
+    tailf:substatement "tailf:cli-before-key";
+    description
+      "This leaf has to be given as a prefix before entering the actual
+      list keys. Very backwards but a construct that exists in some
+      Cisco CLIs.
+
+      The construct can be used also for leaf-lists but only when
+      then tailf:cli-range-list-syntax is also used.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-before-key {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "tailf:cli-prefix-key";
+    description
+      "Specifies before which key the prefix element should be inserted.
+       The first key has number 1.";
+  }
+
+  extension cli-show-with-default {
+    tailf:use-in "leaf";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    description
+      "This leaf will be displayed even when it has its default value.
+      Note that this will somewhat result in a slightly different behaviour
+      when you save a config and then load it again. With this setting
+      in place a leaf that has not been configured will be configured
+      after the load.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-oper-info {
+    argument text {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "rpc";
+    tailf:use-in "identity";
+    tailf:use-in "tailf:action";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "This statement works exactly as tailf:info, with the exception
+      that it is used when displaying the element info in the context
+      of stats.
+
+      Both tailf:info and tailf:cli-oper-info can be present at the same
+      time.";
+  }
+
+  extension cli-case-sensitive {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    description
+      "Specifies that this node is case-sensitive.
+      If applied to a container or a list, any nodes below will
+      also be case-sensitive.
+
+      Note that this will override any case-sensitivity settings
+      configured in confd.conf";
+  }
+
+  extension cli-case-insensitive {
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    description
+      "Specifies that node is case-insensitive.
+      If applied to a container or a list, any nodes below will
+      also be case-insensitive.
+
+      Note that this will override any case-insensitivity settings
+      configured in confd.conf";
+  }
+
+  extension cli-custom-error {
+    argument text {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "This statement specifies a custom error message to be displayed
+      when the user enters an invalid value.";
+  }
+
+  extension cli-full-show-path {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-max-keys";
+    description
+      "Specifies that a path to the show command is considered complete, i.e.,
+      no more elements can be added to the path.  It can also be used to
+      specify a maximum number of keys to be given for lists.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-max-keys {
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    tailf:use-in "tailf:cli-full-show-path";
+    description
+      "Specifies the maximum number of allowed keys for the show command.";
+  }
+
+  extension cli-suppress-show-path {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that the show command cannot be invoked with the path,
+       ie the path is suppressed when auto-rendering show commands for
+       config='false' data.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+
+  extension cli-suppress-show-match {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that a specific completion match (i.e., a filter match that
+      appear at list nodes as an alternative to specifying a single
+      instance) to the show command should not be available.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-list-no {
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the CLI should not accept deletion of the entire list
+      or leaf-list. Only specific instances should be deletable not the
+      entire list in one command. ie, 'no foo <instance>' should be allowed
+      but not 'no foo'.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-no {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that the CLI should not auto-render 'no' commands for
+      this element. An element with this annotation will not appear in the
+      completion list to the 'no' command.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-silent-no {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that the confd.cnof directive cSilentNo should be
+      suppressed for a leaf and that a custom error message should
+      be displayed when the user attempts to delete a non-existing
+      element.
+
+      Used in I- and C-style CLIs.";
+  }
+
+
+  extension cli-full-no {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Specifies that an auto-rendered 'no'-command should be considered complete,
+      ie, no additional leaves or containers can be entered on the same
+      command line.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-incomplete-no {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that an auto-rendered 'no'-command should not be considered
+      complete, ie, additional leaves or containers must be entered on the same
+      command line.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-no-match-completion {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the CLI engine should not provide match completion
+      for the key leafs in the list.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-suppress-show-conf-path {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the show running-config command cannot be invoked with
+       the path, ie the path is suppressed when auto-rendering show running-
+       config commands for config='true' data.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+
+  extension cli-no-key-completion {
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies that the CLI engine should not perform completion for key
+      leafs in the list. This is to avoid querying the data provider
+      for all existing keys.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-instance-info-leafs {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "This statement is used to specifiy how list entries are displayed
+      when doing completion in the CLI.  By default, a list entry is
+      displayed by listing its key values, and the value of a leaf
+      called 'description', if such a leaf exists in the list entry.
+
+      The 'cli-instance-info-leafs' statement takes as its argument a
+      space separated string of leaf names.  When a list entry is
+      displayed, the values of these leafs are concatenated with a
+      space character as separator and shown to the user.
+
+      For example, when asked to specify an interface the CLI will
+      display a list of possible interface instances, say 1 2 3 4.  If
+      the cli-instance-info-leafs property is set to 'description' then
+      the CLI might show:
+
+        Possible completions:
+          1 - internet
+          2 - lab
+          3 - dmz
+          4 - wlan
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-multi-value {
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-max-words";
+    description
+      "Specifies that all remaining tokens on the command line
+      should be considered a value for this leaf. This prevents
+      the need for quoting values containing spaces, but also
+      prevents multiple leaves from being set on the same command
+      line once a multi-value leaf has been given on a line.
+
+      If the tailf:cli-max-words substatements is used then
+      additional leaves may be entered.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-value-display-template {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be used when formating the
+      value of a leaf for display. Note that other leaves cannot
+      be referenced from a display template of one leaf. The only
+      value accessible is the leaf's own value, accessed through
+      $(.).
+
+      See the defintion of cli-template-string for more info.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+
+  extension cli-show-template {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:cli-auto-legend";
+    description
+      "Specifies a template string to be used by the 'show' command in
+      operational mode.  It is primarily intended for displaying
+      non-config data but config data may be included in the template
+      as well.
+
+      See the defintion of cli-template-string for more info.
+
+      Some restrictions includes not applying templates on a leaf that
+      is the key in a list. It is recommended to use the template
+      directly on the list to format the whole list instead.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-auto-legend {
+    tailf:use-in "tailf:cli-show-template";
+    description
+      "Specifies that the legend should be automatically rendered if not "
+        +"already displayed. Useful when using templates for rendering "
+        +"tables.";
+  }
+
+  extension cli-show-template-legend {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "refine";
+    description
+
+      "Specifies a template string to be printed before all list entries are
+      printed.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-show-template-enter {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be printed before each list entry is
+      printed.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-show-template-footer {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+
+      "Specifies a template string to be printed after all list entries are
+      printed.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in J-, I- and C-style CLIs.";
+  }
+
+  extension cli-run-template {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be used by the 'show running-config'
+      command in operational mode.  It is primarily intended for displaying
+      config data but non-config data may be included in the template
+      as well.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-run-template-legend {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+
+      "Specifies a template string to be printed before all list entries are
+      printed.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-run-template-enter {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Specifies a template string to be printed before each list entry is
+      printed.
+
+      When used on a container it only has effect when the container
+      also has a tailf:cli-add-mode, and when tailf:cli-show-no isn't
+      used on the container.
+
+      See the defintion of cli-template-string for more info.
+
+      The variable .reenter is set to 'true' when the 'show configuration'
+      command is executed and the list or container isn't created. This
+      allow, for example, to display
+
+          create foo
+
+      when an instance is created
+
+          edit foo
+
+      when something inside the instance is modified.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  extension cli-run-template-footer {
+    argument value {
+      yin-element true;
+      tailf:arg-type {
+        type tailf:cli-template-string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+
+      "Specifies a template string to be printed after all list entries are
+      printed.
+
+      See the defintion of cli-template-string for more info.
+
+      Used in I- and C-style CLIs.";
+  }
+
+  typedef cli-template-string {
+    type string;
+    description
+      "A template is a text string which is expanded by the CLI engine,
+      and then displayed to the user.
+
+      The template may contain a mix of text and expandable entries.
+      Expandable entries all start with $( and end with a matching ).
+      Parentheses and dollar signs need to be quoted in plain text.
+
+      The template is expanded as follows:
+
+      A parameter is either a relative or absolute path to a leaf
+      element (eg /foo/bar, foo/bar), or one of the builtin variables:
+      .selected, .entered, .legend_shown, .user, .groups, .ip,
+      .display_groups, .path, .ipath or .licounter. In addition the
+      variables .spath and  .ispath are available when a command
+      is executed from a show path.
+
+       .selected
+
+           The .selected variable contains the list of selected paths
+           to be shown. The show template can inspect this element to
+           determine if a given element should be displayed or
+           not. For example:
+
+               $(.selected~=hwaddr?HW Address)
+
+       .entered
+
+           The .entered variable is true if the \"entered\" text has
+           been displayed (either the auto generated text or a
+           showTemplateEnter). This is useful when having a non-table
+           template where each instance should have a text.
+
+               $(.entered?:host $(name))
+
+       .legend_shown
+
+           The .legend_shown variable is true if the \"legend\" text has
+           been displayed (either the auto generated table header or
+           a showTemplateLegend). This is useful to inspect when
+           displaying a table row. If the user enteres the path to a
+           specific instance the builtin table header will not be
+           displayed and the showTemplateLegend will not be invoked
+           and it may be useful to render the legend specifically
+           for this instance.
+
+               $(.legend_shown!=true?Address         Interface)
+
+       .user
+
+           The .user variable contains the name of the current
+           user. This can be used for differentiating the content
+           displayed for a specific user, or in paths. For exapmle:
+
+               $(user{$(.user)}/settings)
+
+       .groups
+
+           The .groups variable contains the a list of groups that the
+           user belongs to.
+
+       .display_groups
+
+           The .display_groups variable contains a list of selected
+           display groups. This can be used to display different
+           content depending on the selected display group. For
+           example:
+
+               $(.display_groups~=details?details...)
+
+       .ip
+
+           The .ip variable contains the ip address that the user
+           connected from.
+
+       .path
+
+           The .path variable contains the path to the entry,
+           formated in CLI style.
+
+       .ipath
+
+           The .ipath variable contains the path to the entry,
+           formated in template style.
+
+       .spath
+
+           The .spath variable contains the show path,
+           formated in CLI style.
+
+       .ispath
+
+           The .ispath variable contains the show path,
+           formated in template style.
+
+       .licounter
+
+           The .licounter variable contains a counter that is
+           incremented for each instance in a list. This means that
+           it will be 0 in the legend, contain the total number of
+           list instances in the footer and something in between in
+           the basic show template.
+
+      $(parameter)
+
+        The value of 'parameter' is substituted.
+
+      $(cond?word1:word2)
+
+        The expansion of 'word1' is substituted if 'cond'
+        evaluates to true, otherwise the expansion of 'word2' is
+        substituted.
+
+        'cond' may be one of
+
+          parameter
+
+            Evaluates to true if the node exists.
+
+          parameter == <value>
+
+            Evaluates to true if the value of the parameter equals
+            <value>.
+
+          parameter != <value>
+
+            Evalutes to true if the value of the parameter does not
+            equal <value>
+
+          parameter ~= <value>
+
+            Provided that the value of the parameter is a list
+            (i.e., the node that the parameter refers to is a
+            leaf-list), this expression evaluates to true if <value>
+            is a member of the list.
+
+      $(parameter|filter)
+
+        The value of 'parameter' processed by 'filter' is
+        substituted. Filters may be either one of the
+        built-ins or a customized filter defined in a
+        callback. See /confdConfig/cli/templateFilter.
+
+        A built-in 'filter' may be one of:
+
+          capfirst
+
+            Capitalizes the first character of the value.
+
+          lower
+
+            Converts the value into lowercase.
+
+          upper
+
+            Converts the value into uppercase.
+
+          filesizeformat
+
+            Formats the value in a human-readable format (e.g.,
+            '13 KB', '4.10 MB',  '102 bytes' etc), where  K
+             means 1024, M means 1024*1024 etc.
+
+            When used without argument the default number of
+            decimals displayed is 2. When used with a numeric
+            integer argument, filesizeformat will display the
+            given number of decimal places.
+
+          humanreadable
+
+            Similar to filesizeformat except no bytes suffix
+            is added  (e.g., '13.00 k', '4.10 M' '102' etc),
+            where k means 1000, M means 1000*1000 etc.
+
+            When used without argument the default number of
+            decimals displayed is 2. When used with a numeric
+            integer argument, humanreadable will display the
+            given number of decimal places.
+
+          commasep
+
+            Separate the numerical values into groups of three
+            digits using a comma (e.g., 1234567 -> 1,234,567)
+
+          hex
+
+            Display integer as hex number. An argument can be
+            used to indicate how many digits should be used in
+            the output. If the hex number is too long it will
+            be truncated at the front, if it is too short it will
+            be padded with zeros at the front. If the width is
+            a negative number then at most that number of digits
+            will be used, but short numbers will not be padded
+            with zeroes. Another argument can be given to indicate
+            if the hex numbers should be written with lower
+            or upper case.
+
+            For example:
+
+              value            Template                       Output
+              12345           {{ value|hex }}                 3039
+              12345           {{ value|hex:2 }}               39
+              12345           {{ value|hex:8 }}               00003039
+              12345           {{ value|hex:-8 }}              3039
+              14911           {{ value|hex:-8:upper }}        3A3F
+              14911           {{ value|hex:-8:lower }}        3a3f
+
+          hexlist
+
+            Display integer as hex number with : between pairs. An
+            argument can be used to indicate how many digits should
+            be used in the output. If the hex number is too long it
+            will be truncated at the front, if it is too short it will
+            be padded with zeros at the front. If the width is
+            a negative number then at most that number of digits
+            will be used, but short numbers will not be padded
+            with zeroes. Another argument can be given to indicate
+            if the hex numbers should be written with lower
+            or upper case.
+
+            For example:
+
+              value            Template                       Output
+              12345           {{ value|hexlist }}             30:39
+              12345           {{ value|hexlist:2 }}           39
+              12345           {{ value|hexlist:8 }}           00:00:30:39
+              12345           {{ value|hexlist:-8 }}          30:39
+              14911           {{ value|hexlist:-8:upper }}    3A:3F
+              14911           {{ value|hexlist:-8:lower }}    3a:3f
+
+          floatformat
+
+            Used for type 'float' in tailf-xsd-types. We recommend
+            that the YANG built-in type 'decimal64' is used instead of
+            'float'.
+
+            When used without an argument, rounds a floating-point
+            number to one decimal place -- but only if there is a
+            decimal part to be displayed.
+
+            For example:
+
+              value           Template                        Output
+              34.23234        {{ value|floatformat }}         34.2
+              34.00000        {{ value|floatformat }}         34
+              34.26000        {{ value|floatformat }}         34.3
+
+            If used with a numeric integer argument, floatformat
+            rounds a number to that many decimal places. For example:
+
+              value           Template                        Output
+              34.23234        {{ value|floatformat:3 }}       34.232
+              34.00000        {{ value|floatformat:3 }}       34.000
+              34.26000        {{ value|floatformat:3 }}       34.260
+
+            If the argument passed to floatformat is negative, it will
+            round a number to that many decimal places -- but only if
+            there's a decimal part to be displayed. For example:
+
+              value           Template                        Output
+              34.23234        {{ value|floatformat:-3 }}      34.232
+              34.00000        {{ value|floatformat:-3 }}      34
+              34.26000        {{ value|floatformat:-3 }}      34.260
+
+            Using floatformat with no argument is equivalent to using
+            floatformat with an argument of -1.
+
+          ljust:width
+
+            Left-align the value given a width.
+
+          rjust:width
+
+            Right-align the value given a width.
+
+          trunc:width
+
+            Truncate value to a given width.
+
+          lower
+
+            Convert the value into lowercase.
+
+          upper
+
+            Convert the value into uppercase.
+
+          show:<dictionary>
+
+            Substitutes the result of invoking the default display
+            function for the parameter. The dictionary can be used
+            for introducing own variables that can be accessed in
+            the same manner as builtin variables. The user defined
+            variables overrides builtin variables. The dictionary
+            is specified as a string on the following form:
+
+              (key=value)(:key=value)*
+
+            For example, with the following expression:
+
+              $(foo|show:myvar1=true:myvar2=Interface)
+
+            the user defined variables can be accessed like this:
+
+              $(.myvar1!=true?Address) $(.myvar2)
+
+            A special case is the dict variable 'indent'. It
+            controls the indentation level of the displayed path.
+            The current indent level can be incremented and
+            decremented using =+ and =-.
+
+            For example:
+
+              $(foobar|show:indent=+2)
+              $(foobar|show:indent=-1)
+              $(foobar|show:indent=10)
+
+            Another special case is he dict variable 'noalign'.
+            It  may be used to suppress the default aligning that
+            may occur when displaying an element.
+
+            For example:
+
+              $(foobar|show:noalign)
+
+          dict:<dictionary>
+
+            Translates the value using the dictionary. Can for
+            example be used for displaying on/off instead of
+            true/false. The dictionary is specified as a string on
+            the following form:
+
+              (key=value)(:key=value)*
+
+            For example, with the following expression:
+
+              $(foo|dict:true=on:false=off)
+
+            if the leaf 'foo' has value 'true', it is displayed as 'on', and
+            if its value is 'false' it is displayed as 'off'.
+
+        Nested invocations are allowed, ie it is possible to have expressions
+        like $((state|dict:yes=Yes:no=No)|rjust:14), or $(/foo{$(../bar)})
+
+
+      For example:
+
+        list interface {
+          key name;
+          leaf name { ... }
+          leaf status { ... }
+          container line {
+            leaf status { ... }
+          }
+          leaf mtu { ... }
+          leaf bw { ... }
+          leaf encapsulation { ... }
+          leaf loopback { ... }
+          tailf:cli-show-template
+            '$(name) is administratively $(status),'
+          + ' line protocol is $(line/status)\\n'
+          + 'MTU $(mtu) bytes, BW $(bw|humanreadable)bit, \\n'
+          + 'Encap $(encapsulation|upper), $(loopback?:loopback not set)\\n';
+        }";
+  }
+
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-common.yang b/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-common.yang
new file mode 100644 (file)
index 0000000..eb71e7e
--- /dev/null
@@ -0,0 +1,3219 @@
+module tailf-common {
+  namespace "http://tail-f.com/yang/common";
+  prefix tailf;
+
+  include tailf-meta-extensions {
+    revision-date 2013-11-07;
+  }
+
+  include tailf-cli-extensions {
+    revision-date 2015-03-19;
+  }
+
+  organization "Tail-f Systems";
+
+  description
+    "This module defines all Tail-f YANG extensions statements
+    and common YANG types.";
+
+  revision 2015-05-22 {
+    description
+      "Released as part of ConfD-5.4.2 / NCS-3.4.2.
+
+       Allow tailf:export and tailf:unique-selector in
+         tailf:annotate-module.";
+  }
+
+  revision 2015-03-19 {
+    description
+      "Released as part of ConfD-5.4 / NCS-3.4.
+
+       Added if-feature as substatement to tailf:annotate.
+
+       Added tailf:no-dependency.
+
+       Updated the description for tailf:dependency.
+
+       Allow tailf:id-value as substatement to 'module',
+       tailf:annotate-module, 'choice', and 'case'.";
+  }
+
+  revision 2014-11-13 {
+    description
+      "Released as part of ConfD-5.3 / NCS-3.3.
+
+       Added tailf:export.";
+  }
+
+  revision 2014-06-30 {
+    description
+      "Released as part of ConfD-5.2 / NCS-3.2.
+
+       Added tailf:sha-256-digest-string and tailf:sha-512-digest-string.";
+  }
+
+  revision 2014-03-27 {
+    description
+      "Released as part of ConfD-5.1 / NCS-3.1.
+
+       Added tailf:actionpoint as substatement to refine.
+       Removed must as substatement to tailf:symlink.";
+  }
+
+  revision 2014-02-20 {
+    description
+      "Released as part of ConfD-5.0.2 / NCS-3.0.2.
+
+       Added tailf:snmp-ned-recreate-when-modified.";
+  }
+
+  revision 2013-12-23 {
+    description
+      "Released as part of ConfD-5.0.1 / NCS-3.0.1.
+
+       Allow 'unique' in tailf:annotate and tailf:annotate-statement.
+
+       Added tailf:snmp-ned-delete-before-create.";
+  }
+
+  revision 2013-11-07 {
+    description
+      "Released as part of ConfD-5.0 / NCS-3.0.
+
+       Allow tailf:code-name as substatement to 'bit'.
+
+       Disallow tailf:id-value as substatement to 'enum'. Use the
+       standard YANG 'value' statement instead.
+
+       Deprecated tailf:hex-list.  Use yang:hex-string instead.
+         There are no plans to remove tailf:hex-list.
+
+       Added the types tailf:ipv4-address-and-prefix-length,
+       tailf:ipv6-address-and-prefix-length, and
+       tailf:ip-address-and-prefix-length,";
+  }
+
+  revision 2013-09-05 {
+    description
+      "Released as part of ConfD-4.3.
+
+       Added tailf:auto-compact as substatement to tailf:indexed-view.";
+  }
+
+  revision 2013-06-14 {
+    description
+      "Released as part of ConfD-4.3.
+
+       Deprecated tailf:symlink.  Use tailf:link instead.
+
+       Allow tailf:alt-name as substatement to tailf:action and rpc.
+
+       Allow status as substatement to tailf:action.
+
+       Allow description in tailf:annotate and tailf:annotate-statement.";
+  }
+
+  revision 2013-05-16 {
+    description
+      "Released as part of ConfD-4.2.2.
+
+       Added tailf:link";
+  }
+
+  revision 2013-03-07 {
+    description
+      "Released as part of ConfD-4.2.
+
+       Allow 'pattern' in tailf:annotate-statement.";
+  }
+
+  revision 2012-11-08 {
+    description
+      "Released as part of ConfD-4.1.
+
+       Added tailf:unique-selector and tailf:unique-leaf.
+
+       Allow tailf:info in bit.
+
+       Allow tailf:code-name as substatement to all statements that
+       define data nodes in the schema tree and the 'rpc',
+       'notification', 'identity', and 'tailf:action' statements.
+
+       Allow status in tailf:symlink";
+  }
+
+  revision 2012-08-23 {
+    description
+      "Released as part of ConfD-4.0.1.
+
+       Allow tailf:cli-operational-mode and tailf:cli-configure-mode in
+         rpc.";
+  }
+
+  revision 2012-06-14 {
+    description
+      "Released as part of ConfD-4.0.
+
+       Added tailf:display-hint.";
+  }
+
+  revision 2012-05-24 {
+    description
+      "Released as part of ConfD-3.9.2.";
+  }
+
+  revision 2012-03-08 {
+    description
+      "Released as part of ConfD-3.9.
+
+      Added tailf:timeout.
+      Added tailf:non-strict-leafref.";
+  }
+
+  revision 2011-12-08 {
+    description
+      "Released as part of ConfD-3.8.
+
+      Allow validation statements in tailf:annotate and
+      tailf:annotate-statement.
+
+      Allow tailf:validate in must, in order to override the evaluation
+      of the must expression with a callback function.
+
+      Disallow tailf:info in range, length and pattern.
+
+      Added tailf:snmp-ned-* statements to control the SNMP NED
+      behavior in NCS.";
+  }
+
+  revision 2011-10-20 {
+    description
+      "Released as part of ConfD-3.7.1.
+
+      Added tailf:priority.";
+  }
+
+  revision 2011-09-22 {
+    description
+      "Released as part of ConfD-3.7.
+
+      Allow tailf:typepoint as substatement to leaf and leaf-list.
+      Allow tailf:id as substatement to tailf:annotate-module.
+      Allow tailf:sort-priority as substatement to tailf:symlink.
+      Added tailf:interruptibale.
+      Added tailf:error-info.
+      Added tailf:snmp-delete-value and tailf:snmp-send-delete-value.
+      Added tailf:step.
+      Added tailf:annotate-statement.
+
+      Clarified how tailf:display-when is evaluated for lists.";
+  }
+
+  revision 2011-08-25 {
+    description
+      "Released as part of ConfD-3.6.2.
+
+      Included latest tailf-cli-extension submodule.";
+  }
+
+  revision 2011-06-30 {
+    description
+      "Released as part of ConfD-3.6.1.
+
+      Clarified what statements are allowed in tailf:annotate and
+      tailf:annotate-module.  Specifically, that 'symlink' and 'action'
+      are not allowed.";
+  }
+
+  revision 2011-05-26 {
+    description
+      "Released as part of ConfD-3.6.
+
+      Allow multiple tailf:snmp-name on leafs that represent MIB scalars.";
+  }
+
+  revision 2011-03-31 {
+    description
+      "Released as part of ConfD-3.5.1.
+
+      Allow tailf:alt-name as substatement to tailf:symlink.";
+  }
+
+  revision 2011-02-24 {
+    description
+      "Released as part of ConfD-3.5.
+
+      Allow tailf:info as substatement to type.
+      Added tailf:writable.
+      Removed the deprecated tailf:cli-default-order statement.
+      Removed the deprecated tailf:instance-info-leafs statement.";
+  }
+
+  revision 2010-11-04 {
+    description
+      "Released as part of ConfD-3.4.
+
+      Added tailf:snmp-exclude-object.
+      Allow tailf:hidden as substatement to tailf:symlink.
+      Allow multiple tailf:hidden statements to be specified on a node.
+      Allow special value '*' ar argument to tailf:annotate.";
+  }
+
+
+  revision 2010-09-16 {
+    description
+      "Released as part of ConfD-3.3.2.
+
+      Included latest tailf-cli-extension submodule.";
+  }
+
+  revision 2010-08-19 {
+    description
+      "Released as part of ConfD-3.3.1.
+
+       Allow multiple tailf:snmp-name statements, and expanded the
+       semantic meaning of this statement.";
+  }
+
+  revision 2010-07-21 {
+    description
+      "Released as part of ConfD-3.3.0.3.
+
+       Added tailf:sort-priority.";
+  }
+
+  revision 2010-06-17 {
+    description
+      "Released as part of ConfD-3.3.
+
+      Added tailf:value-length.
+
+      Added tailf:info-html.
+
+      Added tailf:display-default-order, and deprecated
+      tailf:cli-default-order.
+
+      Added tailf:dependency as a substatement to when.
+
+      Removed the deprecated statements tailf:constant-leaf and
+      tailf:constant-value.";
+  }
+
+  revision 2010-04-22 {
+    description
+      "Released as part of ConfD-3.2.1.
+
+       Added tailf:invocation-mode,
+
+       Fixed bug in tailf:octet-list pattern.";
+  }
+
+  revision 2010-03-18 {
+    description
+      "Released as part of ConfD-3.2.
+
+      Split this module into the main module and two submodules,
+      tailf-meta-extensions, and tailf-cli-extensions.
+
+      Added many tailf:cli- statements in the submodule
+      tailf-cli-extensions.
+
+      Added tailf:info.
+
+      Allow tailf:display-when in tailf:action.
+
+      Added tailf:snmp-lax-type-check.
+
+      Deprecated tailf:instance-info-leafs.  Use
+      tailf:cli-instance-info-leafs instead.
+
+      Removed the argument in tailf:cli-show-no to better match
+      all the other tailf:cli- statements.";
+  }
+
+  revision 2010-01-28 {
+    description
+      "Released as part of ConfD-3.1.1.
+
+      Allow tailf:snmp-oid and tailf:snmp-name in tailf:symlink.
+
+      Added tailf:key-default.
+
+      Allow tailf:suppress-echo in leaf and leaf-list.";
+  }
+
+  revision 2009-12-17 {
+    description
+      "Released as part of ConfD-3.1.
+
+      Added tailf:dependency as a substatement to must.
+
+      Added must and tailf:display-when as children to tailf:symlink.
+
+      Added tailf:interrupt to tailf:exec.
+
+      Allow many tailf statement as substatements to 'refine'.
+
+      Allow tailf:symlink statement in 'augment' and 'case'.
+
+      Added tailf:internal to tailf:actionpoint.
+
+      Deprecated tailf:constant-leaf and tailf:constant-value.";
+  }
+
+  revision 2009-11-06 {
+    description
+      "Released as part of ConfD-3.0.1.
+
+       Added tailf:annotate-module statement.
+
+       Added tailf:code-name statement.
+
+       Clarified the tailf:path-filters statement, and added
+       tailf:no-subtree-match.";
+  }
+
+  revision 2009-10-01 {
+    description
+      "Released as part of ConfD-3.0.
+
+       Clarified that tailf:annotate is applied on the expanded tree.
+       Bugfixes in some typedef patterns.";
+  }
+
+  revision 2009-03-17 {
+    description
+      "Released as part of ConfD-2.8.
+
+      Changed module name from tailf-extensions to reflect the content
+      better.";
+  }
+
+  /*
+   * Common types, natively supported by ConfD.
+   */
+
+  typedef size {
+    type string {
+      pattern
+        "S(\d+G)?(\d+M)?(\d+K)?(\d+B)?";
+    }
+    description
+      "A value that represents a number of bytes.  An example could be
+       S1G8M7K956B; meaning 1GB + 8MB + 7KB + 956B = 1082138556 bytes.
+       The value must start with an S.  Any byte magnifier can be left
+       out, e.g. S1K1B equals 1025 bytes.  The order is significant
+       though, i.e. S1B56G is not a valid byte size.
+
+       In ConfD, a 'size' value is represented as an uint64.";
+  }
+
+  typedef octet-list {
+    type string {
+      pattern '(\d*(.\d*)*)?';
+    }
+    description
+      "A list of dot-separated octets e.g. '192.168.255.1.0'.
+
+      The statement tailf:value-length can be used to restrict the number
+      of octets.  Note that using the 'length' restriction limits the
+      number of characters in the lexical representation.";
+  }
+
+  typedef md5-digest-string {
+    type string;
+    description
+      "The md5-digest-string type automatically computes a MD5 digest for
+      a value adhering to this type.
+
+      This is best explained using an example.  Suppose we have a
+      leaf:
+
+          leaf key {
+              type tailf:md5-digest-string;
+          }
+
+      A valid configuration is:
+
+          <key>$0$In god we trust.</key>
+
+      The '$0$' prefix signals that this is plain text.  When a plain
+      text value is received by the server, an MD5 digest is
+      calculated, and the string '$1$<salt>$' is prepended to the
+      result, where <salt> is a random eight character salt used to
+      generate the digest.  This value is stored in the configuration
+      data store.
+
+      When a value of this type is read, the computed MD5 value is
+      always returned.  In the example above, the following value
+      could be returned:
+
+          <key>$1$fB$ndk2z/PIS0S1SvzWLqTJb.</key>
+
+      If a value starting with '$1$' is received, the server
+      knows that the value already represents an MD5 digest, and
+      stores it as is in the data store.
+
+      A value adhering to this type must have a '$0$' or a
+      '$1$<salt>$' prefix.
+
+      If a default value is specified, it must have a '$1$<salt>$' prefix.
+
+      The digest algorithm used is the same as the md5 crypt function
+      used for encrypting passwords for various UNIX systems, see e.g.
+      http://www.freebsd.org/cgi/cvsweb.cgi/~checkout/~/src/lib/libcrypt/crypt.c
+      ";
+    reference
+      "IEEE Std 1003.1-2008 - crypt() function
+       RFC 1321 - The MD5 Message-Digest Algorithm";
+  }
+
+  typedef sha-256-digest-string {
+    type string {
+      pattern
+        '$0$.*'
+      + '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}';
+    }
+    description
+      "The sha-256-digest-string type automatically computes a SHA-256
+      digest for a value adhering to this type.
+
+      A value of this type matches one of the forms:
+
+        $0$<clear text password>
+        $5$<salt>$<password hash>
+        $5$rounds=<number>$<salt>$<password hash>
+
+      The '$0$' prefix signals that this is plain text. When a plain
+      text value is received by the server, a SHA-256 digest is
+      calculated, and the string '$5$<salt>$' is prepended to the
+      result, where <salt> is a random 16 character salt used to
+      generate the digest.  This value is stored in the configuration
+      data store. The algorithm can be tuned via the
+      /confdConfig/cryptHash/rounds parameter, which if set to a number
+      other than the default will cause '$5$rounds=<number>$<salt>$' to
+      be prepended instead of only '$5$<salt>$'.
+
+      If a value starting with '$5$' is received, the server
+      knows that the value already represents a SHA-256 digest, and
+      stores it as is in the data store.
+
+      If a default value is specified, it must have a '$5$' prefix.
+
+      The digest algorithm used is the same as the SHA-256 crypt function
+      used for encrypting passwords for various UNIX systems, see e.g.
+      http://www.akkadia.org/drepper/SHA-crypt.txt";
+    reference
+      "IEEE Std 1003.1-2008 - crypt() function
+       FIPS.180-3.2008: Secure Hash Standard";
+  }
+
+  typedef sha-512-digest-string {
+    type string {
+      pattern
+        '$0$.*'
+      + '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}';
+    }
+
+    description
+      "The sha-512-digest-string type automatically computes a SHA-512
+      digest for a value adhering to this type.
+
+      A value of this type matches one of the forms:
+
+        $0$<clear text password>
+        $6$<salt>$<password hash>
+        $6$rounds=<number>$<salt>$<password hash>
+
+      The '$0$' prefix signals that this is plain text.  When a plain
+      text value is received by the server, a SHA-512 digest is
+      calculated, and the string '$6$<salt>$' is prepended to the
+      result, where <salt> is a random 16 character salt used to
+      generate the digest.  This value is stored in the configuration
+      data store. The algorithm can be tuned via the
+      /confdConfig/cryptHash/rounds parameter, which if set to a number
+      other than the default will cause '$6$rounds=<number>$<salt>$' to
+      be prepended instead of only '$6$<salt>$'.
+
+      If a value starting with '$6$' is received, the server
+      knows that the value already represents a SHA-512 digest, and
+      stores it as is in the data store.
+
+      If a default value is specified, it must have a '$6$' prefix.
+
+      The digest algorithm used is the same as the SHA-512 crypt function
+      used for encrypting passwords for various UNIX systems, see e.g.
+      http://www.akkadia.org/drepper/SHA-crypt.txt";
+    reference
+      "IEEE Std 1003.1-2008 - crypt() function
+       FIPS.180-3.2008: Secure Hash Standard";
+  }
+
+  typedef des3-cbc-encrypted-string {
+    type string;
+    description
+      "The des3-cbc-encrypted-string type automatically encrypts a value
+      adhering to this type using DES in CBC mode followed by a base64
+      conversion.  If the value isn't encrypted already, that is.
+
+      This is best explained using an example.  Suppose we have a leaf:
+
+          leaf enc {
+              type tailf:des3-cbc-encrypted-string;
+          }
+
+      A valid configuration is:
+
+          <enc>$0$In god we trust.</enc>
+
+      The '$0$' prefix signals that this is plain text.  When a plain
+      text value is received by the server, the value is DES3/Base64
+      encrypted, and the string '$3$' is prepended.  The resulting
+      string is stored in the configuration data store.
+
+      When a value of this type is read, the encrypted value is always
+      returned.  In the example above, the following value could be
+      returned:
+
+          <enc>$3$lyPjszaQq4EVqK7OPOxybQ==</enc>
+
+      If a value starting with '$3$' is received, the server knows
+      that the value is already encrypted, and stores it as is in the
+      data store.
+
+      A value adhering to this type must have a '$0$' or a '$3$' prefix.
+
+      ConfD uses a configurable set of encryption keys to encrypt the
+      string.  For details, see 'encryptedStrings' in the
+      confd.conf(5) manual page.";
+  }
+
+  typedef aes-cfb-128-encrypted-string {
+    type string;
+    description
+      "The aes-cfb-128-encrypted-string works exactly like
+      des3-cbc-encrypted-string but AES/128bits in CFB mode is used to
+      encrypt the string.  The prefix for encrypted values is '$4$'.";
+  }
+
+  typedef ipv4-address-and-prefix-length {
+    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-address-and-prefix-length type represents a combination
+      of an IPv4 address and a prefix length. The prefix length is given
+      by the number following the slash character and must be less than
+      or equal to 32.";
+  }
+
+  typedef ipv6-address-and-prefix-length {
+    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-address-and-prefix-length type represents a combination
+      of an IPv6 address and a prefix length. The prefix length is given
+      by the number following the slash character and must be less than
+      or equal to 128.";
+  }
+
+  typedef ip-address-and-prefix-length {
+    type union {
+      type tailf:ipv4-address-and-prefix-length;
+      type tailf:ipv6-address-and-prefix-length;
+    }
+    description
+      "The ip-address-and-prefix-length type represents a combination of
+      an IP address and a prefix length and is IP version neutral. The
+      format of the textual representations implies the IP version.";
+  }
+
+
+  /*
+   * Meta extensions
+   */
+
+  extension export {
+    argument agent {
+      tailf:arg-type {
+        type union {
+          type enumeration {
+            enum "none";
+            enum "netconf";
+            enum "rest";
+            enum "cli";
+            enum "snmp";
+            enum "webui";
+          }
+          type string;
+        }
+      }
+    }
+    tailf:use-in "module";
+    tailf:occurence "*";
+
+    description
+      "Makes this data model visible in the northbound interface 'agent'.
+
+       This statement makes it possible to have a data model visible
+       through some northbound interface but not others.  For example,
+       if a MIB is used to generate a YANG module, the resulting YANG
+       module can be exposed through SNMP only.
+
+       Use the special agent 'none' to make the data model completely
+       hidden to all notherbound interfaces.
+
+       The agent can also be a free-form string.  In this case, the data
+       model will be visible to maapi applications using this string as its
+       'context'.";
+  }
+
+  extension annotate {
+    argument target {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "module";
+    tailf:use-in "submodule";
+    tailf:occurence "*";
+
+    tailf:substatement "tailf:annotate" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "if-feature" {
+      tailf:occurence "*";
+    }
+    description
+      "Annotates an existing statement with a 'tailf' statement or a
+      validation statement.  This is useful in order to add tailf
+      statements to a module without touching the module source.
+      Annotation statements can be put in a separate annotation
+      module, and then passed to 'confdc' (or 'pyang') when the
+      original module is compiled.
+
+      Any 'tailf' statement, except 'symlink' and 'action' can be
+      annotated.  The statements 'symlink' and 'action' modifies the
+      data model, and are thus not allowed.
+
+      The validation statements 'must', 'min-elements',
+      'max-elements', 'mandatory', 'unique', and 'when' can also be
+      annotated.
+
+      A 'description' can also be annotated.
+
+      'tailf:annotate' can occur on the top-level in a module, or in
+      another 'tailf:annotate' statement.
+
+      The argument is a 'schema-nodeid', i.e. the same as for
+      'augment', or a '*'.  It identifies a target node in the schema
+      tree to annotate with new statements.  The special value '*' can
+      be used within another 'tailf:annotate' statetement, to select all
+      children for annotation.
+
+      The target node is searched for after 'uses' and 'augment'
+      expansion.  All substatements to 'tailf:annotate' are treated as
+      if they were written inline in the target node, with the
+      exception of any 'tailf:annotate' substatements.  These are
+      treated recursively.  For example, the following snippet adds
+      one callpoint to /x and one to /x/y:
+
+        tailf:annotate /x {
+          tailf:callpoint xcp;
+          tailf:annotate y {
+            tailf:callpoint ycp;
+          }
+        }
+      ";
+  }
+
+  extension annotate-module {
+    argument module-name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "module";
+    tailf:occurence "*";
+
+    tailf:substatement "tailf:snmp-oid";
+    tailf:substatement "tailf:snmp-mib-module-name";
+    tailf:substatement "tailf:id";
+    tailf:substatement "tailf:id-value";
+    tailf:substatement "tailf:export";
+    tailf:substatement "tailf:unique-selector";
+    tailf:substatement "tailf:annotate-statement" {
+      tailf:occurence "*";
+    }
+    description
+      "Annotates an existing module or submodule statement with a 'tailf'
+      statement.  This is useful in order to add tailf statements to a
+      module without touching the module source.  Annotation
+      statements can be put in a separate annotation module, and then
+      passed to 'confdc' (or 'pyang') when the original module is
+      compiled.
+
+      'tailf:annotate-module' can occur on the top-level in a module,
+      and is used to add 'tailf' statements to the module statement
+      itself.
+
+      The argument is a name of the module or submodule to annotate.";
+  }
+
+  extension annotate-statement {
+    argument statement-path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:annotate-module";
+    tailf:use-in "tailf:annotate-statement";
+    tailf:occurence "*";
+
+    tailf:substatement "tailf:annotate-statement" {
+      tailf:occurence "*";
+    }
+    description
+      "Annotates an existing statement with a 'tailf' statement, a
+      validation statement, or a type restrcition statement.  This is
+      useful in order to add tailf statements to a module without
+      touching the module source.  Annotation statements can be put in
+      a separate annotation module, and then passed to 'confdc' (or
+      'pyang') when the original module is compiled.
+
+      Any 'tailf' statement, except 'symlink' and 'action' can be
+      annotated.  The statements 'symlink' and 'action' modifies the
+      data model, and are thus not allowed.
+
+      The validation statements 'must', 'min-elements',
+      'max-elements', 'mandatory', 'unique', and 'when' can also be
+      annotated.
+
+      The type restriction statement 'pattern' can also be annotated.
+
+      A 'description' can also be annotated.
+
+      The argument is an XPath-like expression that selects a
+      statement to annotate.  The syntax is:
+
+         <statement-name> ( '[' <arg-name> '=' <arg-value> ']' )
+
+      where <statement-name> is the name of the statement to annotate,
+      and if there are more than one such statement in the parent,
+      <arg-value> is the quoted value of the statement's argument.
+
+      All substatements to 'tailf:annotate-statement' are treated as
+      if they were written inline in the target node, with the
+      exception of any 'tailf:annotate-statement' substatements.
+      These are treated recursively.
+
+      For example, given the grouping:
+
+        grouping foo {
+          leaf bar {
+            type string;
+          }
+          leaf baz {
+            type string;
+          }
+        }
+
+      the following snippet adds a callpoint to the leaf 'baz':
+
+        tailf:annotate-statement grouping[name='foo'] {
+          tailf:annotate-statement leaf[name='baz'] {
+            tailf:callpoint xcp;
+          }
+        }
+      ";
+  }
+
+  /*
+   * Type restriction statements
+   */
+
+  extension value-length {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "type";
+    tailf:substatement "error-message";
+    tailf:substatement "error-app-tag";
+    description
+      "Used only for the types:
+         yang:object-identifier
+         yang:object-identifier-128
+         yang:phys-address
+         yang:hex-string
+         tailf:hex-list
+         tailf:octet-list
+         xs:hexBinary
+
+      This type restriction is used to limit the length of the
+      value-space value of the type.  Note that since all these types are
+      derived from 'string', the standard 'length' statement restricts the
+      lexical representation of the value.
+
+      The argument is a length expression string, with the same syntax as
+      for the standard YANG 'length' statement.";
+  }
+
+  extension path-filters {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "type";
+    tailf:substatement "tailf:no-subtree-match";
+    description
+      "Used for type 'instance-identifier' only.
+
+      The argument is a space separated list of absolute or relative XPath
+      expressions.
+
+      This statement declares that the instance-identifier value must match
+      one of the specified paths, according to the following rules:
+
+        1.  each XPath expression is evaluated, and returns a node set.
+
+        2.  if there is no 'tailf:no-subtree-match' statement, the
+            instance-identifier matches if it refers to a node in this
+            node set, or if it refers to any descendant node of this
+            node set.
+
+        3.  if there is a 'tailf:no-subtree-match' statement, the
+            instance-identifier matches if it refers to a node in this
+            node set.
+
+       For example:
+
+         The value /a/b[key='k1']/c matches the XPath expression
+         /a/b[key='k1']/c.
+
+         The value /a/b[key='k1']/c matches the XPath expression /a/b/c.
+
+         The value /a/b[key='k1']/c matches the XPath expression /a/b, if
+         there is no 'tailf:no-subtree-match' statement.
+
+         The value /a/b[key='k1'] matches the XPath expression /a/b, if
+         there is a 'tailf:no-subtree-match' statement.
+       ";
+  }
+
+  extension step {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "range";
+    description
+      "Used to further restrict the range of integer and decimal types.  The
+       argument is a positive integer or decimal value greater than
+       zero.  The allowed values for the type is further restricted to
+       only those values that matches the expression:
+
+         'low' + n * 'step'
+
+       where 'low' is the lowest allowed value in the range, n is a
+       non-negative integer.
+
+       For example, the following type:
+
+         type int32 {
+           range '-2 .. 9' {
+             tailf:step 3;
+           }
+         }
+
+       has the value space { -2, 1, 4, 7 }";
+  }
+
+  /*
+   * Data implementation statements
+   */
+
+  extension callpoint {
+    argument id {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:use-in "grouping";
+    tailf:occurence "*";
+
+    tailf:substatement "description";
+    tailf:substatement "tailf:config";
+    tailf:substatement "tailf:transform";
+    tailf:substatement "tailf:set-hook";
+    tailf:substatement "tailf:transaction-hook";
+    tailf:substatement "tailf:cache";
+    tailf:substatement "tailf:opaque";
+    tailf:substatement "tailf:internal";
+    description
+      "Identifies a callback in a data provider.  A data provider implements
+      access to external data, either configuration data in a database or
+      operational data.  By default ConfD uses the embedded database
+      (CDB) to store all data. However, some or all of
+      the configuration data may be stored in an external source.  In
+      order for ConfD to be able to manipulate external data, a data
+      provider registers itself using the callpoint id as described in
+      confd_lib_dp(3).
+
+      A callpoint is inherited to all child nodes unless another
+      'callpoint' or an 'cdb-oper' is defined.";
+  }
+
+  extension config {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    description
+      "If this statement is present, the callpoint is applied to nodes with a
+      matching value of their 'config' property.";
+  }
+
+  extension transform {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    description
+      "If set to 'true', the callpoint is a transformation callpoint.  How
+      transformation callpoints are used is described in the
+      'Transformations, Hooks, Hidden Data and Symlinks' chapter
+      in the User's Guide.";
+  }
+
+  extension set-hook {
+    argument value {
+      tailf:arg-type {
+        type enumeration {
+          enum subtree;
+          enum object;
+          enum node;
+        }
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    description
+      "Set hooks are a means to associate user code to the
+      transaction.  Whenever an element gets written, created, or
+      deleted, user code gets invoked and can optionally write more
+      data into the same transaction.
+
+      The difference between set- and transaction hooks are that set
+      hooks are invoked immediately when an element is modified, but
+      transaction hooks are invoked at commit time.
+
+      The value 'subtree' means that all nodes in the configuration
+      below where the hook is defined are affected.
+
+      The value 'object' means that the hook only applies to the list
+      where it is defined, i.e.  it applies to all child nodes that
+      are not themselves lists.
+
+      The value 'node' means that the hook only applies to
+      the node where it is defined and none of its children.
+
+      For more details on hooks,
+      see the 'Transformations, Hooks, Hidden Data and Symlinks'
+      chapter in the User's Guide.";
+  }
+
+  extension transaction-hook {
+    argument value {
+      tailf:arg-type {
+        type enumeration {
+          enum subtree;
+          enum object;
+          enum node;
+        }
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    tailf:substatement "tailf:invocation-mode";
+    description
+      "Transaction hooks are a means to associate user code to the
+      transaction.  Whenever an element gets written, created, or
+      deleted, user code gets invoked and can optionally write more
+      data into the same transaction.
+
+      The difference between set- and transaction hooks are that set
+      hooks are invoked immediately when an element is modified, but
+      transaction hooks are invoked at commit time.
+
+      The value 'subtree' means that all nodes in the configuration
+      below where the hook is defined are affected.
+
+      The value 'object' means that the hook only applies to the list
+      where it is defined, i.e.  it applies to all child nodes that
+      are not themselves lists.
+
+      The value 'node' means that the hook only applies to
+      the node where it is defined and none of its children.
+
+      For more details on hooks,
+      see the 'Transformations, Hooks, Hidden Data and Symlinks'
+      chapter in the User's Guide.";
+  }
+
+  extension invocation-mode {
+    argument value {
+      tailf:arg-type {
+        type enumeration {
+          enum per-operation;
+          enum per-transaction;
+        }
+        default per-operation;
+      }
+    }
+    tailf:use-in "tailf:transaction-hook";
+    description
+      "By default, the node-specific write callbacks (create(), set_elem(),
+      etc) for a transaction hook are invoked for the invidual data nodes
+      that are modified in the transaction. If 'tailf:invocation-mode' is
+      set to 'per-transaction', there will instead be a single invocation
+      of a generic write callback (write_all()).";
+  }
+
+  extension cache {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    tailf:substatement "tailf:timeout";
+    description
+      "If set to 'true', the operational data served by the callpoint will
+      be cached by ConfD.  If set to 'true' in a node that represents
+      configuration data, the statement 'tailf:config' must be present
+      and set to 'false'.  This feature is further described in the section
+      'Caching operational data' in the 'Operational data' chapter in
+      the User's Guide.";
+  }
+
+  extension timeout {
+    argument value {
+      tailf:arg-type {
+        type uint64;
+      }
+    }
+    tailf:use-in "tailf:cache";
+    description
+      "Specifies how long the operational data will be cached, in seconds.
+       This value will override the global value specified via
+       /confdConfig/opcache/timeout in the confd.conf(5) file.";
+  }
+
+  extension opaque {
+    argument value {
+      tailf:arg-type {
+        type string {
+          length "1..255";
+        }
+      }
+    }
+    tailf:use-in "tailf:callpoint";
+    tailf:use-in "tailf:validate";
+    tailf:use-in "tailf:actionpoint";
+    description
+      "Defines an opaque string which is passed to the callback function
+      in the context.";
+  }
+
+  extension id {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "module";
+    description
+      "This statement is used when old confspec models are translated to
+      YANG.  It needs to be present if systems deployed with data
+      based on confspecs are updated to YANG based data models.
+
+      In confspec, the 'id' of a data model was a string that never
+      would change, even if the namespace URI would change.  It is not
+      needed in YANG, since the namespace URi cannot change as a module
+      is updated.
+
+      This statement is typically present in YANG modules generated by
+      cs2yang.  If no live upgrade needs to be done from a confspec
+      based system to a YANG based system, this statement can be
+      removed from such a generated module.";
+  }
+
+  extension cdb-oper {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:substatement "description";
+    tailf:substatement "tailf:persistent";
+    description
+      "Indicates that operational data nodes below this node are stored in
+      CDB.";
+  }
+
+  extension persistent {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:cdb-oper";
+    description
+      "If it is set to 'true', the operational data is stored on disk.  If
+      set to 'false', the operational data is not persistent across
+      ConfD restarts.  The default is 'false'.";
+  }
+
+  extension id-value {
+    argument value {
+      tailf:arg-type {
+        type uint32 {
+          range "1..4294967294";
+        }
+      }
+    }
+    tailf:use-in "module";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "rpc";
+    tailf:use-in "identity";
+    tailf:use-in "notification";
+    tailf:use-in "choice";
+    tailf:use-in "case";
+    tailf:use-in "tailf:action";
+    description
+      "This statement lets you specify a hard wired numerical id value to
+      associate with the parent node.  This id value is normally auto
+      generated by confdc and is used when working with the ConfD API
+      to refer to a tag name, to avoid expensive string comparison.
+      Under certain rare circumstances this auto generated hash value
+      may collide with a hash value generated for a node in another
+      data model.  Whenever such a collision occurs the ConfD daemon
+      fails to start and instructs the developer to use the 'id-value'
+      statement to resolve the collision.
+
+
+      A thorough discussion on id-value can be found in the section Hash
+      Values and the id-value Statement in the YANG chapter in the User
+      Guide.";
+  }
+
+  extension default-ref {
+    argument path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "refine";
+    description
+      "This statement defines a dynamic default value.  It is a reference to
+      some other leaf in the datamodel.  If no value has been set for
+      this leaf, it defaults to the value of the leaf that the
+      'default-ref' argument points to.
+
+      The textual format of a 'default-ref' is an XPath location path with
+      no predicates.
+
+      The type of the leaf with a 'default-ref' will be set to the
+      type of the referred leaf.  This means that the type statement in
+      the leaf with the 'default-ref' is ignored, but it SHOULD match the
+      type of the referred leaf.
+
+      Here is an example, where a group without a 'hold-time' will get as
+      default the value of another leaf up in the hierarchy:
+
+        leaf hold-time {
+            mandatory true;
+            type int32;
+        }
+        list group {
+            key 'name';
+            leaf name {
+                type string;
+            }
+            leaf hold-time {
+                type int32;
+                tailf:default-ref '../../hold-time';
+            }
+        }
+      ";
+  }
+
+  extension sort-order {
+    argument how {
+      tailf:arg-type {
+        type enumeration {
+          enum normal {
+            description
+              "Entries are sorted on the key values.";
+          }
+          enum snmp {
+            description
+              "All string key values are considered to
+              start with a length byte for the purpose of sorting.";
+          }
+          enum snmp-implied {
+            description
+              "As 'snmp', but uses a length byte for all except the last key.";
+          }
+          enum unsorted {
+            description
+              "Entries do not have any special order.  Note that it is
+               not possible to use the function 'find_next' on an
+               unsorted list.  If an unsorted list is filtered (e.g.,
+               in the CLI, the entire list must be traversed.
+
+               If this value is given for a list stored in CDB, it
+               has no effect.";
+          }
+        }
+        default normal;
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "leaf-list";
+    tailf:use-in "tailf:secondary-index";
+    description
+      "This statement can be used for 'ordered-by system' lists and
+       leaf-lists only.  It indicates in which way the list entries
+       are sorted.";
+  }
+
+  extension link {
+    argument target {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:substatement "tailf:inherit-set-hook";
+    description
+      "This statement specifies that the data node should be
+       implemented as a link to another data node, called the target
+       data node.  This means that whenever the node is modified, the
+       system modifies the target data node instead, and whenever the
+       data node is read, the system returns the value of target data
+       node.
+
+       Note that if the data node is a leaf, the target node MUST also
+       be a leaf, and if the data node is a leaf-list, the target node
+       MUST also be a leaf-list.
+
+       Note that the type of the data node MUST be the same as the
+       target data node.  Currently the compiler cannot check this.
+
+       The argument is an XPath absolute location path.  If
+       the target lies within lists, all keys must be specified.
+       A key either has a value, or is a reference to a key in the path of the
+       source node, using the function current() as starting
+       point for an XPath location path.  For example:
+
+         /a/b[k1='paul'][k2=current()/../k]/c";
+  }
+
+  extension lower-case {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    description
+      "Use for config false leafs and leaf-lists only.
+
+       This extension serves as a hint to the system that the
+       leaf's type has the implict pattern '[^A-Z]*', i.e., all
+       strings returned by the data provider are lower case (in
+       the 7-bit ASCII range).
+
+       The CLI uses this hint when it is run in case-insensitive mode
+       to optimize the lookup calls towards the data provider.";
+  }
+
+  extension inherit-set-hook {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+        default "false";
+      }
+    }
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "tailf:link";
+    description
+      "This statement specifies that a 'tailf:set-hook' statement should
+       survive through symlinks. If set to true a set hook gets called as
+       soon as the value is set via a symlink but also during commit. The
+       normal behaviour is to only call the set hook during commit time.";
+  }
+
+  extension secondary-index {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "list";
+    tailf:occurence "*";
+    tailf:substatement "tailf:index-leafs" {
+      tailf:occurence "1";
+    }
+    tailf:substatement "tailf:sort-order";
+    tailf:substatement "tailf:display-default-order";
+    description
+      "This statement creates a secondary index with a given name in the
+      parent list.  The secondary index can be used to control the
+      displayed sort order of the instances of the list.
+
+      Read more about sort order in 'The ConfD Command-Line Interface
+      (CLI)' chapters in the User Guide, confd_lib_dp(3), and
+      confd_lib_maapi(3).
+
+      NOTE: Currently secondary-index is not supported for config false
+      data stored in CDB.";
+  }
+
+  extension index-leafs {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:secondary-index";
+    tailf:occurence "1";
+    description
+      "This statement contains a space separated list of leaf names.  Each
+      such leaf must be a direct child to the list.  The secondary
+      index is kept sorted according to the values of these leafs.";
+  }
+
+  extension typepoint {
+    argument id {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "typedef";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:occurence "*";
+    description
+      "If a typedef, leaf, or leaf-list has a 'typepoint' statement, a
+      user-defined type is specified, as opposed to a derivation or
+      specification of an existing type.  The implementation of a
+      user-defined type must be provided in the form of a shared object
+      with C callback functions that is loaded into the ConfD daemon at
+      startup time. Read more about user-defined types in the
+      confd_types(3) manual page.
+
+      The argument defines the ID associated with a typepoint.  This
+      ID is provided by the shared object, and used by the ConfD
+      daemon to locate the implementation of a specific user-defined
+      type.";
+  }
+
+  /*
+   * Validation related statements
+   */
+
+  extension unique-selector {
+    argument context-path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "module";
+    tailf:use-in "submodule";
+    tailf:use-in "grouping";
+    tailf:use-in "augment";
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:occurence "*";
+
+    tailf:substatement "tailf:unique-leaf" {
+      tailf:occurence "+";
+    }
+    description
+      "The standard YANG statement 'unique' can be used to check for
+       uniqueness within a single list only.  Specifically, it cannot
+       be used to check for uniqueness of leafs within a sublist.
+
+       For example:
+
+         container a {
+           list b {
+             ...
+             unique 'server/ip server/port';
+             list server {
+               ...
+               leaf ip { ... };
+               leaf port { ... };
+             }
+           }
+         }
+
+       The unique expression above is not legal.  The intention is
+       that there must not be any two 'server' entries in any 'b' with
+       the same combination of ip and port.  This would be illegal:
+
+         <a>
+           <b>
+             <name>b1</name>
+             <server>
+               <ip>10.0.0.1</ip>
+               <port>80</port>
+             </server>
+           </b>
+           <b>
+             <name>b2</name>
+             <server>
+               <ip>10.0.0.1</ip>
+               <port>80</port>
+             </server>
+           </b>
+         </a>
+
+       With 'tailf:unique-selector' and 'tailf:unique-leaf', this kind
+       of constraint can be defined.
+
+       The argument to 'tailf:unique-selector' is an XPath descendant
+       location path (matches the rule 'descendant-schema-nodeid' in
+       RFC 6020).  The first node in the path MUST be a list node, and
+       it MUST be defined in the same module as the
+       tailf:unique-selector.  For example, the following is illegal:
+
+         module y {
+           ...
+           import x {
+             prefix x;
+           }
+           tailf:unique-selector '/x:server' { // illegal
+             ...
+           }
+         }
+
+       For each instance of the node where the selector is defined, it
+       is evaluated, and for each node selected by the selector, a
+       tuple is constructed by evaluating the 'tailf:unique-leaf'
+       expression.  All such tuples must be unique.  If a
+       'tailf:unique-leaf' expression refers to a non-existing leaf,
+       the corresponding tuple is ignored.
+
+       In the example above, the unique expression can be replaced by:
+
+         container a {
+           tailf:unique-selector 'b/server' {
+             tailf:unique-leaf 'ip';
+             tailf:unique-leaf 'port';
+           }
+           list b {
+             ...
+           }
+         }
+
+       For each container 'a', the XPath expression 'b/server' is
+       evaluated.  For each such server, a 2-tuple is constructed with
+       the 'ip' and 'port' leafs.  Each such 2-tuple is guaranteed to
+       be unique.";
+  }
+
+  extension unique-leaf {
+    argument leaf-expr {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:unique-selector";
+    tailf:occurence "+";
+    description
+      "See 'tailf:unique-selector' for a description of how this statement
+       is used.
+
+       The argument is an XPath descendant location path (matches the
+       rule 'descendant-schema-nodeid' in RFC 6020), and it MUST refer to
+       a leaf.";
+  }
+
+  extension validate {
+    argument id {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "grouping";
+    tailf:use-in "refine";
+    tailf:use-in "must";
+    tailf:occurence "*";
+    tailf:substatement "description";
+    tailf:substatement "tailf:call-once";
+    tailf:substatement "tailf:dependency" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "tailf:opaque";
+    tailf:substatement "tailf:internal";
+    tailf:substatement "tailf:priority";
+    description
+      "Identifies a validation callback which is invoked when a configuration
+      value is to be validated.  The callback validates a value and
+      typically checks it towards other values in the data store.
+      Validation callbacks are used when the YANG built-in validation
+      constructs ('must', 'unique') are not expressive enough.
+
+      Callbacks use the API described in confd_lib_maapi(3) to
+      access whatever other configuration values needed to perform the
+      validation.
+
+      Validation callbacks are typically assigned to individual nodes
+      in the data model, but it may be feasible to use a single
+      validation callback on a root node.  In that case the callback
+      is responsible for validation of all values and their
+      relationships throughout the data store.
+
+      The 'validate' statment should in almost all cases have a
+      'tailf:dependency' substatement.  If such a statement is not
+      given, the validate function is evaluated at every commit,
+      leading to overall performance degradation.
+
+      If the 'validate' statement is defined in a 'must' statement,
+      validation callback is called instead of evaluating the must
+      expression.  This is useful if the evaluation of the must statement
+      uses too much resources, and the condition expressed with the must
+      statement is easier to check with a validation callback function.";
+  }
+
+  extension call-once {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:validate";
+    description
+      "This optional statement can be used only if the parent statement is
+      a list.  If 'call-once' is 'true'. the validation callback is
+      only called once even though there exists many list entries in
+      the data store.  This is useful if we have a huge amount of
+      instances or if values assigned to each instance have to be
+      validated in comparison with its siblings.";
+  }
+
+  extension dependency {
+    argument path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "must";
+    tailf:use-in "when";
+    tailf:use-in "tailf:validate";
+    tailf:substatement "tailf:xpath-root";
+    tailf:occurence "*";
+    description
+      "This statement is used to specify that the must or when expression
+      or validation function depends on a set of subtrees in the data
+      store.  Whenever a node in one of those subtrees are modified,
+      the must or when expression is evaluated, or validation code executed.
+
+      The textual format of a 'dependency' is an XPath location path with
+      no predicates.
+
+      If the node that declares the dependency is a leaf, there is an
+      implicit dependency to the leaf itself.
+
+      For example, with the leafs below, the validation code for'vp'
+      will be called whenever 'a' or 'b' is modified.
+
+        leaf a {
+            type int32;
+            tailf:validate vp {
+                tailf:dependency '../b';
+            }
+        }
+        leaf b {
+            type int32;
+        }
+
+       For 'when' and 'must' expressions, the compiler can derive the
+       dependencies automatically from the XPath expression in most
+       cases.  The exception is if any wildcards are used in the expression.
+
+       For 'when' expressions to work, a 'tailf:dependency' statement
+       must be given, unless the compiler can figure out the dependency
+       by itself.
+
+       Note that having 'must' expressions or a 'tailf:validate'
+       statement without dependencies impacts the overall performance
+       of the system, since all such 'must' expressions or validation
+       functions are evaluated at every commit.";
+  }
+
+  extension no-dependency {
+    tailf:use-in "must";
+    tailf:use-in "tailf:validate";
+    tailf:occurence "?";
+    description
+      "This optional statements can be used to explicitly say that a 'must'
+      expression or a validation function is evaluated at every
+      commit.  Use this with care, since the overall performance of
+      the system is impacted if this statement is used.";
+  }
+
+  extension priority {
+    tailf:use-in "tailf:validate";
+    argument value {
+      tailf:arg-type {
+        type uint32;
+      }
+    }
+    description
+      "This extension takes an integer parameter specifying the order
+       validation code will be evaluated, in order of increasing
+       priority.
+
+       The default priority is 0.";
+  }
+
+  extension no-subtree-match {
+    tailf:use-in "tailf:path-filters";
+    description
+      "See tailf:path-filters.";
+  }
+
+
+  /*
+   * User interface related statements
+   */
+
+  extension info {
+    argument text {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "typedef";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "rpc";
+    tailf:use-in "identity";
+    tailf:use-in "type";
+    tailf:use-in "enum";
+    tailf:use-in "bit";
+    tailf:use-in "length";
+    tailf:use-in "pattern";
+    tailf:use-in "range";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:action";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "tailf:cli-exit-command";
+    description
+      "Contains a textual description of the definition, suitable for
+      being presented to the CLI and WebUI users.
+
+      The first sentence of this textual description is used in the
+      CLI as a summary, and displayed to the user when a short
+      explanation is presented.
+
+      The 'description' statement is related, but targeted to the module
+      reader, rather than the CLI or WebUI user.
+
+      The info string may contain a ';;' keyword. It is used in type
+      descriptions for leafs when the builtin type info needs to be
+      customized.  A 'normal' info string describing a type is assumed
+      to contain a short textual description.  When ';;' is present it
+      works as a delimiter where the text before the keyword is
+      assumed to contain a short description and the text after the
+      keyword a long(er) description.  In the context of completion in
+      the CLI the text will be nicely presented in two columns where
+      both descriptions are aligned when displayed.";
+  }
+
+  extension info-html {
+    argument text {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "rpc";
+    tailf:use-in "identity";
+    tailf:use-in "tailf:action";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "This statement works exactly as 'tailf:info', with the exception
+      that it can contain HTML markup.  The WebUI will display the
+      string with the HTML markup, but the CLI will remove all HTML markup
+      before displaying the string to the user.  In most cases,
+      using this statement avoids using special descriptions in webspecs
+      and clispecs.
+
+      If this statement is present, 'tailf:info' cannot be given at the same
+      time.";
+  }
+
+  extension sort-priority {
+    argument value {
+      tailf:arg-type {
+        type int32;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "This extension takes an integer parameter specifying the order and
+       can be placed on leafs, containers, lists and leaf-lists.
+       When showing, or getting configuration, leaf values will be returned
+       in order of increasing sort-priority.
+
+       The default sort-priority is 0.";
+  }
+
+  extension writable {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "leaf";
+    description
+      "This extension makes operational data (i.e., config false data)
+       writable.  Only valid for leafs.";
+  }
+
+  extension suppress-echo {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "typedef";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    description
+      "If this statetement is set to 'true', leafs of this type will not have
+      their values echoed when input in the webui or when the CLI prompts
+      for the value. The value will also not be included in the audit
+      log in clear text but will appear as ***.";
+  }
+
+  extension hidden {
+    argument tag {
+      tailf:arg-type {
+        type string {
+          pattern '[^\*].*|..+'; // must not be single '*'
+// YANG 1.1:
+//          pattern '\*' {
+//            modifier invert-match;
+//          }
+        }
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:action";
+    tailf:use-in "refine";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "rpc";
+    tailf:occurence "*";
+
+    description
+      "This statement can be used to hide a node from some, or all,
+      northbound interfaces.  All nodes with the same value are
+      considered a hide group and are treated the same with regards to
+      being visible or not in a northbound interface.
+
+      A node with an hidden property is not shown in the northbound
+      user interfaces (CLI and Web UI) unless an 'unhide' operation has
+      been performed in the user interface.
+
+      The hidden value 'full' indicates that the node should be hidden
+      from all northbound interfaces, including programmatical interfaces
+      such as NETCONF.
+
+      The value '*' is not valid.
+
+      A hide group can be unhidden only if this has been explicitly
+      allowed in the confd.conf(5) daemon configuration.
+
+      Multiple hide groups can be specified by giving this statement
+      multiple times.  The node is shown if any of the specified hide groups
+      has been given in the 'unhide' operation.
+
+      Note that if a mandatory node is hidden, a hook callback
+      function (or similar) might be needed in order to set the
+      element.";
+  }
+
+  extension display-when {
+    argument condition {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:xpath-root";
+    description
+      "The argument contains an XPath expression which specifies when
+      the node should be displayed in the CLI and WebUI.  For example,
+      when the CLI performs completion, and one of the candidates is
+      a node with a 'display-when' expression, the expression is
+      evaluated by the CLI.  If the XPath expression evaluates to
+      true, the node is shown as a possible completion candidate,
+      otherwise not.
+
+      For a list, the display-when expression is evaluated once for the
+      entire list.  In this case, the XPath context node is the list's parent
+      node.
+
+      This feature is further described in the 'Transformations, Hooks,
+      Hidden Data and Symlinks' chapter in the User Guide.";
+  }
+
+  extension display-groups {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "This property is used in the CLI when 'enableDisplayGroups' has been
+      set to true in the confd.conf(5) file.  Display groups are used
+      to control which elements should be displayed by the show command.
+
+      The argument is a space-separated string of tags.
+
+      In the J-style CLI the 'show status', 'show table' and 'show
+      all' commands use display groups.  In the C- and I-style
+      CLIs the 'show <pattern>' command uses display groups.
+
+      If no display groups are specified when running the commands, the
+      node will be displayed if it does not have the 'display-groups'
+      property, or if the property value includes the special value 'none'.
+
+      If display groups are specified when running the command, then
+      the node will be displayed only if its 'display-group'
+      property contains one of the specified display groups.";
+  }
+
+  extension display-default-order {
+    tailf:use-in "tailf:secondary-index";
+    description
+      "Specifies that the list should be displayed sorted according
+      to this secondary index in the show command.
+
+      If the list has more than one secondary index,
+      'display-default-order' must be present in one index only.
+
+      Used in J-, I- and C-style CLIs and WebUI.";
+  }
+
+  extension alt-name {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "rpc";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "This property is used to specify an alternative name for the
+      node in the CLI.  It is used instead of the node name in the CLI,
+      both for input and output.";
+  }
+
+  extension display-status-name {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "This property is used to specify an alternative name for the
+      element in the CLI.  It is used when displaying status
+      information in the C- and I-style CLIs.";
+  }
+
+  extension display-column-name {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "refine";
+    description
+      "This property is used to specify an alternative column name for the
+      leaf in the CLI.  It is used when displaying the leaf in a
+      table in the CLI.";
+  }
+
+  extension display-hint {
+    argument hint {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "typedef";
+    description
+      "This statement can be used to add a display-hint to a leaf or
+       typedef of type binary.  The display-hint is used in the CLI
+       and WebUI instead of displaying the binary as a base64-encoded
+       string.  It is also used for input.
+
+       The value of a 'display-hint' is defined in RFC 2579.
+
+       For example, with the display-hint value '1x:', the value is
+       printed and inputted as a colon-separated hex list.";
+  }
+
+  /*
+   * SNMP mapping statements
+   */
+
+  extension snmp-oid {
+    argument oid {
+      tailf:arg-type {
+        type tailf:tailf-oid;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "module";
+    tailf:use-in "refine";
+    description
+      "Used when the YANG module is mapped to an SNMP module.
+
+      If this statement is present as a direct child to 'module',
+      it indicates the top level OID for the module.
+
+      When the parent node is mapped to an SNMP object, this statement
+      specifies the OID of the SNMP object.  It may be either a full
+      OID or just a suffix (a period, followed by an integer).  In the
+      latter case, a full OID must be given for some ancestor element.
+
+      NOTE: when this statement is set in a list, it refers to the OID of
+      the correspondig table, not the table entry.";
+  }
+
+  extension snmp-name {
+    argument name {
+      tailf:arg-type {
+        type tailf:snmp-identifier;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "enum";
+    tailf:use-in "refine";
+    tailf:occurence "*";
+    description
+      "Used when the YANG module is mapped to an SNMP module.
+
+      When the parent node is mapped to an SNMP object, this statement
+      specifies the name of the SNMP object.
+
+      If the parent node is mapped to multiple SNMP objects, this
+      statement can be given multiple times.  The first statement
+      specifies the primary table.
+
+      In a list, the argument is interpreted as:
+
+             [MIB-MODULE-NAME:]TABLE-NAME
+
+      For a leaf representing a table column, it is interpreted as:
+
+             [[MIB-MODULE-NAME:]TABLE-NAME:]NAME
+
+      For a leaf representing a scalar variable, it is interpreted as:
+
+             [MIB-MODULE-NAME:]NAME
+
+      If a YANG list is mapped to multiple SNMP tables, each such SNMP
+      table must be specified with a 'tailf:snmp-name' statement.  If
+      the table is defined in another MIB than the MIB specified in
+      'tailf:snmp-mib-module-name', the MIB name must be specified in this
+      argument.
+
+      A leaf in a list that is mapped to multiple SNMP tables must specify
+      the name of the table it is mapped to if it is different from the
+      primary table.
+
+      In the following example, a single YANG list 'interface' is mapped
+      to the MIB tables ifTable, ifXTable, and ipv4InterfaceTable:
+
+        list interface {
+          key index;
+          tailf:snmp-name 'ifTable'; // primary table
+          tailf:snmp-name 'ifXTable';
+          tailf:snmp-name 'IP-MIB:ipv4InterfaceTable';
+
+          leaf index {
+            type int32;
+          }
+          leaf description {
+            type string;
+            tailf:snmp-name 'ifDescr';  // mapped to primary table
+          }
+          leaf name {
+            type string;
+            tailf:snmp-name 'ifXTable:ifName';
+          }
+          leaf ipv4-enable {
+            type boolean;
+            tailf:snmp-name
+              'IP-MIB:ipv4InterfaceTable:ipv4InterfaceEnableStatus';
+          }
+          ...
+        }
+
+      When emitting a mib from yang, enum labels are used as-is if they
+      follow the SMI rules for labels (no '.' or '_' characters and beginning
+      with a lowercase letter). Any label that doesn't satisfy the SMI rules
+      will be converted as follows:
+
+        An initial uppercase character will be downcased.
+
+        If the initial character is not a letter it will be prepended with
+        an 'a'.
+
+        Any '.' or '_' characters elsewhere in the label will be substituted
+        with '-' characters.
+
+        In the resulting label, any multiple '-' character sequence will be
+        replaced with a single '-' character.
+
+      If this automatic conversion is not suitable, snmp-name can be used
+      to specify the label to use when emitting a MIB.";
+  }
+
+  extension snmp-mib-module-name {
+    argument name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "module";
+    tailf:use-in "refine";
+    description
+      "Used when the YANG module is mapped to an SNMP module.
+
+      Specifies the name of the SNMP MIB module where the SNMP objects
+      are defined.
+
+      This property is inherited by all child nodes.";
+  }
+
+  extension snmp-row-status-column {
+    argument value {
+      tailf:arg-type {
+        type uint32 {
+          range "1..max";
+        }
+      }
+    }
+    tailf:use-in "list";
+    tailf:use-in "refine";
+    description
+      "Used when an SNMP module is generated from the YANG module.
+
+      When the parent list node is mapped to an SNMP table, this
+      statement specifies the column number of the generated RowStatus
+      column.  If it is not specified, the generated RowStatus column
+      will be the last in the table.";
+  }
+
+  extension snmp-lax-type-check {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "leaf";
+    description
+      "Normally, the ConfD MIB compiler checks that the data type of an SNMP
+      object matches the data type of the corresponding YANG leaf.  If
+      both objects are writeble, the data types need to precisely
+      match, but if the SNMP object is read-only, or if
+      snmp-lax-type-check is set to 'true', the compiler accepts the
+      object if the SNMP type's value space is a superset of the YANG
+      type's value space.
+
+      If snmp-lax-type-check is true and the MIB object is writable, the SNMP
+      agent will reject values outside the YANG data type range in runtime.";
+  }
+
+  extension snmp-exclude-object {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "tailf:symlink";
+    tailf:use-in "refine";
+    description
+      "Used when an SNMP MIB is generated from a YANG module, using
+      the --generate-oids option to confdc.
+
+      If this statement is present, confdc will exclude this object
+      from the resulting MIB.";
+  }
+
+  extension snmp-delete-value {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:substatement "tailf:snmp-send-delete-value";
+    description
+      "This statement is used to define a value to be used in SNMP
+       to delete an optional leaf.  The argument to this statement is the
+       special value.  This special value must not be part of the value
+       space for the YANG leaf.
+
+       If the optional leaf does not exists, reading it over SNMP returns
+       'noSuchInstance', unless the statement 'tailf:snmp-send-delete-value'
+       is used, in which case the same value as used to delete the node
+       is returned.
+
+       For example, the YANG leaf:
+
+            leaf opt-int {
+              type int32 {
+                range '1..255';
+              }
+              tailf:snmp-delete-value 0 {
+                tailf:snmp-send-delete-value;
+              }
+            }
+
+       can be mapped to a SMI object with syntax:
+
+            SYNTAX       Integer32 (0..255)
+
+       Setting such an object to '0' over SNMP will delete the node
+       from the datastore.  If the node does not exsist, reading it over
+       SNMP will return '0'.";
+  }
+
+  extension snmp-send-delete-value {
+    tailf:use-in "tailf:snmp-delete-value";
+    description
+      "See tailf:snmp-delete-value.";
+  }
+
+  /*
+   * SNMP NED statements
+   */
+
+  extension snmp-ned-set-before-row-modification {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    description
+      "If this statement is present on a leaf, it tells the SNMP NED
+       that if a column in the row is modified, and it is marked with
+       'tailf:snmp-ned-modification-dependent', then the column marked
+       with 'tailf:snmp-ned-set-before-modification' needs to be set to
+       <value> before the other column is modified.  After all such
+       columns have been modified, the column marked with
+       'tailf:snmp-ned-set-before-modification' is reset to its initial
+       value.";
+  }
+
+  extension snmp-ned-modification-dependent {
+    tailf:use-in "leaf";
+    description
+      "This statement is used on all columns in a table that
+       require the usage of the column marked with
+       tailf:snmp-ned-set-before-row-modification.
+
+       This statement can be used on any column in a table where one
+       leaf is marked with tailf:snmp-ned-set-before-row-modification,
+       or a table that AUGMENTS such a table, or a table with a
+       foreign index in such a table.";
+  }
+
+  extension snmp-ned-accessible-column {
+    argument leaf-name {
+      tailf:arg-type {
+        type union {
+          type tailf:identifier;
+          type int32;
+        }
+      }
+    }
+    tailf:use-in "list";
+    description
+      "The name or subid number of an accessible column that is
+       instantiated in all table entries in a table.  The column does
+       not have to be writable.  The SNMP NED will use this column
+       when it uses GET-NEXT to loop through the list entries, and
+       when doing existence tests.
+
+       If this column is not given, the SNMP NED uses the following
+       algorithm:
+
+         1.  If there is a RowStatus column, it will be used.
+         2.  If an INDEX leaf is accessible, it will be used.
+         3.  Otherwise, use the first accessible column returned
+             by the SNMP agent.";
+  }
+
+  extension snmp-ned-delete-before-create {
+    tailf:use-in "list";
+    description
+      "This statement is used in a list to make the SNMP NED always send
+       deletes before creates.  Normally, creates are sent before deletes.";
+  }
+
+  extension snmp-ned-recreate-when-modified {
+    tailf:use-in "list";
+    description
+      "This statement is used in a list to make the SNMP NED delete
+       and recreate the row when a column in the row is modified.";
+  }
+
+  /*
+   * Java code generation statements
+   */
+
+  extension java-class-name {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "refine";
+    description
+      "Used to give another name than the default name to generated Java
+      classes.  This statemement is typically used to avoid name conflicts
+      in the Java classes.";
+  }
+
+  /*
+   * Common code generation statemements
+   */
+
+  extension code-name {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "enum";
+    tailf:use-in "bit";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "rpc";
+    tailf:use-in "identity";
+    tailf:use-in "notification";
+    tailf:use-in "tailf:action";
+    description
+      "Used to give another name to the enum or node name in generated
+      header files. This statement is typically used to avoid name
+      conflicts if there is a data node with the same name as the
+      enumeration, if there are multiple enumerations in different
+      types with the same name but different values, or if there are
+      multiple node names that are mapped to the same name in the
+      header file.";
+  }
+
+  /*
+   * Data modeling extensions
+   */
+
+  extension action {
+    argument name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "augment";
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "grouping";
+    tailf:occurence "*";
+
+    tailf:substatement "description";
+    tailf:substatement "input";
+    tailf:substatement "output";
+    tailf:substatement "status";
+    tailf:substatement "tailf:actionpoint";
+    tailf:substatement "tailf:alt-name";
+    tailf:substatement "tailf:cli-mount-point";
+    tailf:substatement "tailf:cli-configure-mode";
+    tailf:substatement "tailf:cli-operational-mode";
+    tailf:substatement "tailf:cli-oper-info";
+    tailf:substatement "tailf:code-name";
+    tailf:substatement "tailf:confirm-text";
+    tailf:substatement "tailf:display-when";
+    tailf:substatement "tailf:exec";
+    tailf:substatement "tailf:hidden";
+    tailf:substatement "tailf:info";
+    tailf:substatement "tailf:info-html";
+    description
+      "Defines an action (method) in the data model.
+
+      When the action is invoked, the instance on which the action is
+      invoked is explicitly identified by an hierarchy of
+      configuration or state data.
+
+      The action statement can have either a 'tailf:actionpoint' or a
+      'tailf:exec' substatement.  If the action is implemented as a
+      callback in an application daemon, 'tailf:actionpoint' is used,
+      whereas 'tailf:exec' is used for an action implemented as a
+      standalone executable (program or script).  Additionally, 'action'
+      can have the same substatements as the standard YANG 'rpc'
+      statement, e.g., 'description', 'input', and 'output'.
+
+      For example:
+
+           container sys {
+             list interface {
+               key name;
+               leaf name {
+                 type string;
+               }
+               tailf:action reset {
+                 tailf:actionpoint my-ap;
+                 input {
+                   leaf after-seconds {
+                     mandatory false;
+                     type int32;
+                   }
+                 }
+               }
+             }
+           }
+
+      We can also add a 'tailf:confirm-text', which defines a string to
+      be used in the user interfaces to prompt the user for
+      confirmation before the action is executed.  The optional
+      'tailf:confirm-default' and 'tailf:cli-batch-confirm-default' can be set
+      to control if the default is to proceed or to abort. The latter will only
+      be used during batch processing in the CLI (e.g. non-interactive mode).
+
+           tailf:action reset {
+             tailf:actionpoint my-ap;
+             input {
+               leaf after-seconds {
+                 mandatory false;
+                 type int32;
+               }
+             }
+             tailf:confirm-text 'Really want to do this?' {
+               tailf:confirm-default true;
+             }
+           }
+
+       The 'tailf:actionpoint' statement can have a 'tailf:opaque'
+       substatement, to define an opaque string that is passed to the
+       callback function.
+
+           tailf:action reset {
+             tailf:actionpoint my-ap {
+               tailf:opaque 'reset-interface';
+             }
+             input {
+               leaf after-seconds {
+                 mandatory false;
+                 type int32;
+               }
+             }
+           }
+
+       When we use the 'tailf:exec' substatement, the argument to exec
+       specifies the program or script that should be executed.  For
+       example:
+
+           tailf:action reboot {
+             tailf:exec '/opt/sys/reboot.sh' {
+               tailf:args '-c $(context) -p $(path)';
+             }
+             input {
+               leaf when {
+                 type enumeration {
+                   enum now;
+                   enum 10secs;
+                   enum 1min;
+                 }
+               }
+             }
+           }";
+  }
+
+  extension actionpoint {
+    argument name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "rpc";
+    tailf:use-in "tailf:action";
+    tailf:use-in "refine";
+    tailf:substatement "tailf:opaque";
+    tailf:substatement "tailf:internal";
+    description
+      "Identifies the callback in a data provider that implements the
+      action.  See confd_lib_dp(3) for details on the API.";
+  }
+
+  extension confirm-text {
+    argument text {
+      yin-element true;
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "rpc";
+    tailf:use-in "tailf:action";
+    tailf:substatement "tailf:confirm-default";
+    tailf:substatement "tailf:cli-batch-confirm-default";
+    description
+      "A string which is used in the user interfaces to prompt the user for
+      confirmation before the action is executed. The optional
+      'confirm-default' and 'cli-batch-confirm-default' can be set to control
+      if the default is to proceed or to abort. The latter will only
+      be used during batch processing in the CLI (e.g. non-interactive mode).";
+  }
+
+  extension confirm-default {
+    argument name {
+      tailf:arg-type {
+        type boolean;
+      }
+    }
+    tailf:use-in "tailf:confirm-text";
+    description
+      "Specifies if the default is to proceed or abort the action when a
+      confirm-text is set.  If this value is not specified, a ConfD
+      global default value can be set in clispec(5).";
+  }
+
+  extension indexed-view {
+    tailf:use-in "list";
+    tailf:substatement "tailf:auto-compact";
+    description
+      "This element can only be used if the list has a single key of
+      an integer type.
+
+      It is used to signal that lists instances uses an indexed view,
+      i.e., making it possible to insert a new list entry at a certain
+      position.  If a list entry is inserted at a certain position, list
+      entries following this position are automatically renumbered by the
+      system, if needed, to make room for the new entry.
+
+      This statement is mainly provided for backwards compatibility with
+      confspecs.  New data models should consider using YANG's ordered-by
+      user statement instead.";
+  }
+
+  extension auto-compact {
+    tailf:use-in "tailf:indexed-view";
+    description
+      "If an indexed-view list is marked with this statement, it means that
+       the server will automatically renumber entires after a delete
+       operation so that the list entries are strictly monotonically
+       increasing, starting from 1, with no holes.  New list entries
+       can either be insterted anywhere in the list, or created at the
+       end; but it is an error to try to create a list entry with a
+       key that would result in a hole in the sequence.
+
+       For example, if the list has entries 1,2,3 it is an error to
+       create entry 5, but correct to create 4.";
+  }
+
+  extension key-default {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "leaf";
+    description
+      "Must be used for key leafs only.
+
+      Specifies a value that the CLI and WebUI will use when a list entry is
+      created, and this key leaf is not given a value.
+
+      If one key leaf has a key-default value, all key leafs that
+      follow this key leaf must also have key-default values.";
+  }
+
+  extension error-info {
+    tailf:use-in "module";
+    tailf:use-in "submodule";
+    tailf:occurence "?";
+
+    tailf:substatement "description";
+    tailf:substatement "leaf" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "leaf-list" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "list" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "container" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "choice" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "uses" {
+      tailf:occurence "*";
+    }
+    description
+      "Declares a set of data nodes to be used in the NETCONF <error-info>
+      element.
+
+      A data provider can use one of the
+      confd_*_seterr_extended_info() functions (see confd_lib_dp(3))
+      to set these data nodes on errors.
+
+      This statement may be used multiple times.
+
+      For example:
+
+          tailf:error-info {
+             leaf severity {
+               type enumeration {
+                 enum info;
+                 enum error;
+                 enum critical;
+               }
+             }
+             container detail {
+               leaf class {
+                 type uint8;
+               }
+               leaf code {
+                 type uint8;
+               }
+             }
+           }";
+  }
+
+  extension non-strict-leafref {
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:substatement "path" {
+      tailf:occurence "1";
+    }
+    description
+      "This statement can be used in leafs and leaf-lists similar
+       to 'leafref', but allows reference to non-existing leafs,
+       and allows reference from config to non-config.
+
+       This statement takes no argument, but expects the core YANG
+       statement 'path' as a substatement.  The function 'deref' cannot
+       be used in the path, since it works on nodes of type leafref
+       only.
+
+       The type of the leaf or leaf-list must be exactly the same
+       as the type of the target.
+
+       This statement can be viewed as a substitute for a standard
+       'require-instance false' on leafrefs, which isn't allowed.
+
+       The CLI uses this statement to provide completion with
+       existing values, and the WebUI uses it to provide a
+       drop-down box with existing values.";
+  }
+
+  /*
+   * RPC and action implementation statements
+   */
+
+  extension exec {
+    argument cmd {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "rpc";
+    tailf:use-in "tailf:action";
+
+    tailf:substatement "tailf:args";
+    tailf:substatement "tailf:uid";
+    tailf:substatement "tailf:gid";
+    tailf:substatement "tailf:wd";
+    tailf:substatement "tailf:global-no-duplicate";
+    tailf:substatement "tailf:raw-xml";
+    tailf:substatement "tailf:interruptible";
+    tailf:substatement "tailf:interrupt";
+    description
+      "Specifies that the rpc or action is implemented as an OS executable.
+      The argument 'cmd' is the path to the executable file.  If the
+      command is in the $PATH of ConfD, the 'cmd' can be just the name
+      of the executable.";
+  }
+
+  extension args {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies arguments to send to the executable when it is invoked by
+      ConfD.  The argument 'value' is a space separated list of
+      argument strings.  It may contain variables on the form
+      $(variablename).  These variables will be expanded before the
+      command is executed.  The following variables are always available:
+
+        $(user)   The name of the user which runs the operation.
+
+        $(groups) A comma separated string of the names of the groups
+                  the user belongs to.
+
+        $(ip)     The source ip address of the user session.
+
+        $(uid)    The user id of the user.
+
+        $(gid)    The group id of the user.
+
+      When the parent 'exec' statement is a substatement of 'action', the
+      following additional variablenames are available:
+
+        $(keypath)   The path that identifies the parent container of 'action'
+                     in string keypath form, e.g.,
+                     '/sys:host{earth}/interface{eth0}'.
+
+        $(path)      The path that identifies the parent container of 'action'
+                     in CLI path form, e.g., 'host earth interface eth0'.
+
+        $(context)   cli | webui | netconf | any string provided by MAAPI
+
+      For  example:
+         args '-user $(user) $(uid)';
+      might expand to:
+         -user bob 500
+      ";
+  }
+
+  extension raw-xml {
+    tailf:use-in "tailf:exec";
+    tailf:substatement "tailf:batch";
+    description
+      "Specifies that ConfD should not convert the RPC XML parameters to
+      command line arguments.  Instead, ConfD just passes the raw XML on
+      stdin to the program.
+
+      This statement is not allowed in 'tailf:action'.";
+  }
+
+  extension interruptible {
+    argument value {
+      tailf:arg-type {
+        type boolean;
+        default "true";
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies whether the client can abort the
+      execution of the executable.";
+  }
+
+  extension interrupt {
+    argument signal {
+      tailf:arg-type {
+        type enumeration {
+          enum sigkill;
+          enum sigint;
+          enum sigterm;
+        }
+        default "sigkill";
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "This statement specifies which signal is sent to executable by ConfD
+      in case the client terminates or aborts the execution.
+
+      If not specified, 'sigkill' is sent.";
+  }
+
+
+  extension uid {
+    argument value {
+      tailf:arg-type {
+        type union {
+          type enumeration {
+            enum confd {
+              description
+                "The command is run as the same user id as the ConfD daemon.";
+            }
+            enum user {
+              description
+                "The command is run as the same user id as the user logged
+                in to ConfD.  This user id MUST exist as an actual user id
+                in the underlying operating system.";
+            }
+            enum root {
+              description
+                "The command is run as root.";
+            }
+          }
+          type uint32;
+        }
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies which user id to use when executing the command.
+
+      If 'uid' is an integer value, the command is run as the user with
+      this user id.
+
+      If 'uid' is set to either 'user', 'root' or an integer user id, the
+      ConfD daemon must have been started as root (or setuid), or the
+      ConfD executable program 'cmdwrapper' must have setuid root
+      permissions.";
+  }
+
+  extension gid {
+    argument value {
+      tailf:arg-type {
+        type union {
+          type enumeration {
+            enum confd {
+              description
+                "The command is run as the same group id as the ConfD daemon.";
+            }
+            enum user {
+              description
+                "The command is run as the same group id as the user logged
+                in to ConfD.  This group id MUST exist as an actual group id
+                in the underlying operating system.";
+            }
+            enum root {
+              description
+                "The command is run as root.";
+            }
+          }
+          type uint32;
+        }
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies which group id to use when executing the command.
+
+      If 'gid' is an integer value, the command is run as the group with
+      this group id.
+
+      If 'gid' is set to either 'user', 'root' or an integer group id, the
+      ConfD daemon must have been started as root (or setuid), or the
+      ConfD executable program 'cmdwrapper' must have setuid root
+      permissions.";
+  }
+
+  extension wd {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies which working directory to use when executing the
+      command. If not given the command is executed from the homedir
+      of the user logged in to ConfD.";
+  }
+
+  extension global-no-duplicate {
+    argument value {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "tailf:exec";
+    description
+      "Specifies that only one instance with the same name can be run at any
+      one time in the system. The command can be started either from
+      the CLI, the WebUI or through NETCONF. If a client tries to
+      execute this command while another operation with the same
+      'global-no-duplicate' name is running, a 'resource-denied' error is
+      generated.";
+  }
+
+  extension batch {
+    tailf:use-in "tailf:raw-xml";
+    description
+      "Specifies that the command returns immediately, but still runs in the
+      background.";
+  }
+
+  /*
+   * Deprecated types
+   */
+
+  typedef hex-list {
+    type string {
+      pattern '(([0-9a-fA-F]){2}(:([0-9a-fA-F]){2})*)?';
+    }
+    status deprecated;
+    description
+      "DEPRECATED: Use yang:hex-string instead.  There are no plans to remove
+       tailf:hex-list.
+
+      A list of colon-separated hexa-decimal octets e.g. '4F:4C:41:71'.
+
+      The statement tailf:value-length can be used to restrict the number
+      of octets.  Note that using the 'length' restriction limits the
+      number of characters in the lexical representation.";
+  }
+
+  /*
+   * Deprecated statements
+   */
+
+  extension symlink {
+    argument name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    status deprecated;
+    tailf:use-in "list";
+    tailf:use-in "container";
+    tailf:use-in "module";
+    tailf:use-in "submodule";
+    tailf:use-in "augment";
+    tailf:use-in "case";
+    tailf:occurence "*";
+
+    tailf:substatement "status";
+    tailf:substatement "tailf:alt-name";
+    tailf:substatement "tailf:cli-add-mode";
+    tailf:substatement "tailf:cli-allow-join-with-key";
+    tailf:substatement "tailf:cli-allow-join-with-value";
+    tailf:substatement "tailf:cli-allow-key-abbreviation";
+    tailf:substatement "tailf:cli-allow-range";
+    tailf:substatement "tailf:cli-allow-wildcard";
+    tailf:substatement "tailf:cli-autowizard";
+    tailf:substatement "tailf:cli-boolean-no";
+    tailf:substatement "tailf:cli-break-sequence-commands";
+    tailf:substatement "tailf:cli-column-align";
+    tailf:substatement "tailf:cli-column-stats";
+    tailf:substatement "tailf:cli-column-width";
+    tailf:substatement "tailf:cli-compact-stats";
+    tailf:substatement "tailf:cli-compact-syntax";
+    tailf:substatement "tailf:cli-completion-actionpoint";
+    tailf:substatement "tailf:cli-custom-error";
+    tailf:substatement "tailf:cli-custom-range";
+    tailf:substatement "tailf:cli-custom-range-actionpoint";
+    tailf:substatement "tailf:cli-custom-range-enumerator";
+    tailf:substatement "tailf:cli-delayed-auto-commit";
+    tailf:substatement "tailf:cli-delete-container-on-delete";
+    tailf:substatement "tailf:cli-delete-when-empty";
+    tailf:substatement "tailf:cli-diff-dependency" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "tailf:cli-disabled-info";
+    tailf:substatement "tailf:cli-disallow-value";
+    tailf:substatement "tailf:cli-display-empty-config";
+    tailf:substatement "tailf:cli-display-separated";
+    tailf:substatement "tailf:cli-drop-node-name";
+    tailf:substatement "tailf:cli-no-keyword";
+    tailf:substatement "tailf:cli-enforce-table";
+    tailf:substatement "tailf:cli-embed-no-on-delete";
+    tailf:substatement "tailf:cli-exit-command";
+    tailf:substatement "tailf:cli-explicit-exit";
+    tailf:substatement "tailf:cli-expose-key-name";
+    tailf:substatement "tailf:cli-expose-ns-prefix";
+    tailf:substatement "tailf:cli-flat-list-syntax";
+    tailf:substatement "tailf:cli-flatten-container";
+    tailf:substatement "tailf:cli-full-command";
+    tailf:substatement "tailf:cli-full-no";
+    tailf:substatement "tailf:cli-full-show-path";
+    tailf:substatement "tailf:cli-hide-in-submode";
+    tailf:substatement "tailf:cli-ignore-modified";
+    tailf:substatement "tailf:cli-incomplete-command";
+    tailf:substatement "tailf:cli-incomplete-no";
+    tailf:substatement "tailf:cli-incomplete-show-path";
+    tailf:substatement "tailf:cli-instance-info-leafs";
+    tailf:substatement "tailf:cli-key-format";
+    tailf:substatement "tailf:cli-list-syntax";
+    tailf:substatement "tailf:cli-min-column-width";
+    tailf:substatement "tailf:cli-mode-name";
+    tailf:substatement "tailf:cli-mode-name-actionpoint";
+    tailf:substatement "tailf:cli-multi-value";
+    tailf:substatement "tailf:cli-multi-word-key";
+    tailf:substatement "tailf:cli-multi-line-prompt";
+    tailf:substatement "tailf:cli-no-key-completion";
+    tailf:substatement "tailf:cli-no-match-completion";
+    tailf:substatement "tailf:cli-no-name-on-delete";
+    tailf:substatement "tailf:cli-no-value-on-delete";
+    tailf:substatement "tailf:cli-oper-info";
+    tailf:substatement "tailf:cli-optional-in-sequence";
+    tailf:substatement "tailf:cli-prefix-key";
+    tailf:substatement "tailf:cli-preformatted";
+    tailf:substatement "tailf:cli-range-delimiters";
+    tailf:substatement "tailf:cli-range-list-syntax";
+    tailf:substatement "tailf:cli-recursive-delete";
+    tailf:substatement "tailf:cli-remove-before-change";
+    tailf:substatement "tailf:cli-reset-container";
+    tailf:substatement "tailf:cli-run-template";
+    tailf:substatement "tailf:cli-run-template-enter";
+    tailf:substatement "tailf:cli-run-template-footer";
+    tailf:substatement "tailf:cli-run-template-legend";
+    tailf:substatement "tailf:cli-sequence-commands";
+    tailf:substatement "tailf:cli-show-config";
+    tailf:substatement "tailf:cli-show-no";
+    tailf:substatement "tailf:cli-show-order-tag";
+    tailf:substatement "tailf:cli-show-order-taglist";
+    tailf:substatement "tailf:cli-show-template";
+    tailf:substatement "tailf:cli-show-template-enter";
+    tailf:substatement "tailf:cli-show-template-footer";
+    tailf:substatement "tailf:cli-show-template-legend";
+    tailf:substatement "tailf:cli-show-with-default";
+    tailf:substatement "tailf:cli-strict-leafref";
+    tailf:substatement "tailf:cli-suppress-key-abbreviation";
+    tailf:substatement "tailf:cli-suppress-key-sort";
+    tailf:substatement "tailf:cli-suppress-list-no";
+    tailf:substatement "tailf:cli-suppress-mode";
+    tailf:substatement "tailf:cli-suppress-no";
+    tailf:substatement "tailf:cli-suppress-range";
+    tailf:substatement "tailf:cli-suppress-shortenabled";
+    tailf:substatement "tailf:cli-suppress-show-conf-path";
+    tailf:substatement "tailf:cli-suppress-show-match";
+    tailf:substatement "tailf:cli-suppress-show-path";
+    tailf:substatement "tailf:cli-suppress-silent-no";
+    tailf:substatement "tailf:cli-suppress-validation-warning-prompt";
+    tailf:substatement "tailf:cli-suppress-wildcard";
+    tailf:substatement "tailf:cli-table-footer";
+    tailf:substatement "tailf:cli-table-legend";
+    tailf:substatement "tailf:cli-trim-default";
+    tailf:substatement "tailf:cli-value-display-template";
+    tailf:substatement "tailf:display-when";
+    tailf:substatement "tailf:hidden" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "tailf:inherit-set-hook";
+    tailf:substatement "tailf:info";
+    tailf:substatement "tailf:info-html";
+    tailf:substatement "tailf:path" {
+      tailf:occurence "1";
+    }
+    tailf:substatement "tailf:snmp-exclude-object";
+    tailf:substatement "tailf:snmp-name" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "tailf:snmp-oid";
+    tailf:substatement "tailf:sort-priority";
+    description
+      "DEPRECATED: Use tailf:link instead.  There are no plans to remove
+       tailf:symlink.
+
+      This statement defines a 'symbolic link' from a node to some other node.
+      The argument is the name of the new node, and the mandatory substatement
+      'tailf:path' points to the node which is linked to.";
+  }
+
+  extension path {
+    argument path {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    status deprecated;
+    tailf:occurence "1";
+    tailf:use-in "tailf:symlink";
+    description
+      "This statement specifies which node a symlink points to.
+
+      The textual format of a symlink is an XPath absolute location path. If
+      the target lies within lists, all keys must be specified.
+      A key either has a value, or is a reference to a key in the path of the
+      source node, using the function current() as starting
+      point for an XPath location path.  For example:
+
+      /a/b[k1='paul'][k2=current()/../k]/c
+      ";
+  }
+
+  /*
+   * Tail-f internal statements
+   */
+
+  extension internal {
+    tailf:use-in "tailf:callpoint";
+    tailf:use-in "tailf:validate";
+    tailf:use-in "tailf:actionpoint";
+    description
+      "For internal ConfD / NCS use only.";
+  }
+
+  extension junos-val-as-xml-tag {
+    tailf:use-in "leaf";
+    description
+      "Internal extension to handle non-YANG JUNOS data models.
+      Use only for key enumeration leafs.";
+  }
+
+  extension junos-val-with-prev-xml-tag {
+    tailf:use-in "leaf";
+    description
+      "Internal extension to handle non-YANG JUNOS data models.
+      Use only for keys where previous key is marked with
+      'tailf:junos-val-as-xml-tag'.";
+  }
+
+  extension xpath-root {
+    argument value {
+      tailf:arg-type {
+        type uint8;
+      }
+    }
+    tailf:use-in "must";
+    tailf:use-in "when";
+    tailf:use-in "path";
+    tailf:use-in "tailf:display-when";
+    tailf:use-in "tailf:cli-diff-dependency";
+    description
+      "Internal extension to 'chroot' XPath expressions";
+  }
+
+  extension ncs-device-type {
+    argument type {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "container";
+    tailf:use-in "list";
+    tailf:use-in "leaf";
+    tailf:use-in "leaf-list";
+    tailf:use-in "refine";
+    description
+      "Internal extension to tell NCS what type of device the data model
+      is used for.";
+  }
+
+  extension structure {
+    argument name {
+      tailf:arg-type {
+        type tailf:identifier;
+      }
+    }
+    tailf:use-in "module";
+    tailf:use-in "submodule";
+    tailf:occurence "*";
+
+    tailf:substatement "description";
+    tailf:substatement "leaf" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "leaf-list" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "list" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "container" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "choice" {
+      tailf:occurence "*";
+    }
+    tailf:substatement "uses" {
+      tailf:occurence "*";
+    }
+    description
+      "Internal extension to define a data structure without any semantics
+       attached.";
+  }
+
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-meta-extensions.yang b/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/netconf/tailf-meta-extensions.yang
new file mode 100644 (file)
index 0000000..1581d52
--- /dev/null
@@ -0,0 +1,133 @@
+submodule tailf-meta-extensions {
+
+  belongs-to tailf-common {
+    prefix tailf;
+  }
+
+  organization "Tail-f Systems";
+
+  description
+    "This submodule defines Tail-f YANG meta extensions statements.";
+
+  revision 2013-11-07 {
+    description
+      "Released as part of ConfD-5.0.
+
+       Added tailf:occurrence.";
+  }
+
+  revision 2010-08-19 {
+    description
+      "Released as part of ConfD-3.3.1.
+
+       Added tailf:snmp-identifier.";
+  }
+
+  revision 2010-03-18 {
+    description
+      "Released as part of ConfD-3.2.";
+  }
+
+  /*
+   * Types used to describe the extension statements' arguments.
+   */
+
+  typedef identifier {
+    type string {
+      pattern "[A-Za-z_][A-Za-z0-9_-]*";
+    }
+  }
+
+  typedef snmp-identifier {
+    type string {
+      pattern "[A-Za-z_][A-Za-z0-9_-]*(:[A-Za-z_][A-Za-z0-9_-]*)*";
+    }
+  }
+
+  typedef tailf-oid {
+    type string {
+      pattern "(([0-1](\.[1-3]?[0-9]))"
+            + "|(2.(0|([1-9]\d*)))"
+            + "|([A-Za-z_][A-Za-z0-9_-]*))?"
+            + "(\.(0|([1-9]\d*)))+";
+    }
+  }
+
+  /*
+   * Descriptive meta extensions
+   */
+
+  extension use-in {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "extension";
+    tailf:occurence "*";
+    description
+      "Specifies in which statements a particular extension statement can be
+      used.";
+  }
+
+  extension substatement {
+    argument name {
+      tailf:arg-type {
+        type string;
+      }
+    }
+    tailf:use-in "extension";
+    tailf:occurence "*";
+
+    tailf:substatement "tailf:occurence";
+    description
+      "Specifies which statements can occur as substatement to the
+      given statement.";
+  }
+
+  extension arg-type {
+    tailf:use-in "argument";
+    tailf:substatement "type" {
+      tailf:occurence "1";
+    }
+    tailf:substatement "default";
+    description
+      "Specifies the type of the argument.";
+  }
+
+  extension occurence {
+    argument value {
+      tailf:arg-type {
+        type enumeration {
+          enum "QuestionMark" {
+            description
+              "The extenstion may be given zero or one time.
+               This is the default.";
+          }
+          enum "*" {
+            description
+              "The extenstion may be given zero or multiple times.";
+          }
+          enum "+" {
+            description
+              "The extenstion must be given at least once.";
+          }
+          enum "1" {
+            description
+              "The extenstion must be given exactly once.";
+          }
+        }
+      }
+    }
+    tailf:use-in "extension";
+    description
+      "Specifices how an extension statement may be used.
+
+       If this statement is given as a substatement to 'extension',
+       it applies to all 'use-in' statements.
+
+       If this statement is given as a substatement to 'tailf:substatement',
+       it applies to this substatement.";
+  }
+
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/sfc-ios-xe-impl.yang b/sfc-renderers/sfc-ios-xe-renderer/src/main/yang/sfc-ios-xe-impl.yang
new file mode 100755 (executable)
index 0000000..2871a61
--- /dev/null
@@ -0,0 +1,53 @@
+module sfc-ios-xe-impl {
+
+  yang-version 1;
+  namespace "urn:opendaylight:params:xml:ns:yang:controller:config:sfc-ios-xe:impl";
+  prefix "sfc-ios-xe-impl";
+
+  import config { prefix config; revision-date 2013-04-05; }
+  import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
+
+
+  description
+      "This module contains the base YANG definitions for
+      sfc-ios-xe implementation.";
+
+  revision "2014-10-20" {
+      description
+          "Initial revision.";
+  }
+
+  // This is the definition of the service implementation as a module identity
+  identity sfc-ios-xe-impl {
+      base config:module-type;
+
+      // Specifies the prefix for generated java classes.
+      config:java-name-prefix SfcIosXe;
+  }
+
+
+  // Augments the 'configuration' choice node under modules/module.
+  augment "/config:modules/config:module/config:configuration" {
+    case sfc-ios-xe-impl {
+      when "/config:modules/config:module/config:type = 'sfc-ios-xe-impl'";
+
+      //wires in the data-broker service
+      container data-broker {
+        uses config:service-ref {
+          refine type {
+              mandatory false;
+              config:required-identity mdsal:binding-async-data-broker;
+          }
+        }
+      }
+      container binding-registry {
+        uses config:service-ref {
+          refine type {
+              mandatory true;
+              config:required-identity mdsal:binding-broker-osgi-registry;
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessorTest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeRspProcessorTest.java
new file mode 100644 (file)
index 0000000..a829a93
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.provider.OpendaylightSfc;
+import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
+import org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffDataPlaneLocatorName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHopBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.READ_PATH;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_FUNCTION;
+
+public class IosXeRspProcessorTest extends AbstractDataBrokerTest {
+
+    private final OpendaylightSfc odl = new OpendaylightSfc();
+    private final String forwarderName = "forwarder";
+    private final String firstFunctionName = "firstFunction";
+    private final String secondFunctionName = "secondFunction";
+    private final String thirdFunctionName = "thirdFunction";
+    private final String mgmtIp = "10.0.0.1";
+    private DataBroker dataBroker;
+    private NodeManager nodeManager;
+
+    @Before
+    public void init() {
+        dataBroker = getDataBroker();
+        odl.setDataProvider(dataBroker);
+        nodeManager = mock(NodeManager.class);
+        prepareSfcEntities();
+    }
+
+    @Test
+    public void updateRsp() {
+        when(nodeManager.getMountpointFromIpAddress(new IpAddress(new Ipv4Address(mgmtIp)))).thenReturn(dataBroker);
+
+        IosXeRspProcessor processor = new IosXeRspProcessor(dataBroker, nodeManager);
+        processor.updateRsp(createTestRenderedServicePath());
+
+        verify(nodeManager, times(1)).getMountpointFromIpAddress(new IpAddress(new Ipv4Address(mgmtIp)));
+
+        // Read and test created service path
+        ServicePath servicePath = (ServicePath) new IosXeDataStoreAPI(dataBroker, new ServicePathKey(10L), READ_PATH,
+                LogicalDatastoreType.CONFIGURATION).call();
+        assertNotNull(servicePath);
+        ConfigServiceChainPathMode chainPathMode = servicePath.getConfigServiceChainPathMode();
+        assertTrue(chainPathMode.getServiceIndex().getServices().size() == 4);
+    }
+
+    private RenderedServicePath createTestRenderedServicePath() {
+        RenderedServicePathBuilder renderedServicePathBuilder = new RenderedServicePathBuilder();
+        // Prepare hops
+        List<RenderedServicePathHop> hops = new ArrayList<>();
+        RenderedServicePathHopBuilder firstHop = new RenderedServicePathHopBuilder();
+        firstHop.setServiceFunctionForwarder(new SffName(forwarderName))
+                .setServiceFunctionName(new SfName(firstFunctionName));
+        RenderedServicePathHopBuilder secondHop = new RenderedServicePathHopBuilder();
+        secondHop.setServiceFunctionForwarder(new SffName(forwarderName))
+                .setServiceFunctionName(new SfName(secondFunctionName));
+        RenderedServicePathHopBuilder thirdHop = new RenderedServicePathHopBuilder();
+        thirdHop.setServiceFunctionForwarder(new SffName(forwarderName))
+                .setServiceFunctionName(new SfName(thirdFunctionName));
+        hops.add(firstHop.build());
+        hops.add(secondHop.build());
+        hops.add(thirdHop.build());
+        renderedServicePathBuilder.setName(new RspName("testRsp"))
+                .setKey(new RenderedServicePathKey(new RspName("testRsp")))
+                .setPathId(10L)
+                .setStartingIndex((short) 255)
+                .setRenderedServicePathHop(hops);
+        return renderedServicePathBuilder.build();
+    }
+
+    private void prepareSfcEntities() {
+        // First SFF
+        ServiceFunctionForwarderBuilder serviceForwarderBuilder = new ServiceFunctionForwarderBuilder();
+        List<SffDataPlaneLocator> sffDataPlaneLocators = new ArrayList<>();
+        SffDataPlaneLocatorBuilder sffDataPlaneLocatorBuilder = new SffDataPlaneLocatorBuilder();
+        DataPlaneLocatorBuilder dataPlaneLocatorBuilder = new DataPlaneLocatorBuilder();
+        String dplIp = "100.0.0.1";
+        dataPlaneLocatorBuilder.setLocatorType(new IpBuilder().setIp(new IpAddress(new Ipv4Address(dplIp))).build());
+        String sffDpl = "sffDpl";
+        sffDataPlaneLocatorBuilder.setName(new SffDataPlaneLocatorName(sffDpl))
+                .setKey(new SffDataPlaneLocatorKey(new SffDataPlaneLocatorName(sffDpl)))
+                .setDataPlaneLocator(dataPlaneLocatorBuilder.build());
+        sffDataPlaneLocators.add(sffDataPlaneLocatorBuilder.build());
+        serviceForwarderBuilder.setName(new SffName(forwarderName))
+                .setKey(new ServiceFunctionForwarderKey(new SffName(forwarderName)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(mgmtIp)))
+                .setSffDataPlaneLocator(sffDataPlaneLocators);
+
+        // First SF
+        ServiceFunctionBuilder firstServiceFunctionBuilder = new ServiceFunctionBuilder();
+        firstServiceFunctionBuilder.setName(firstFunctionName)
+                .setKey(new ServiceFunctionKey(firstFunctionName));
+        // Second SF
+        ServiceFunctionBuilder secondServiceFunctionBuilder = new ServiceFunctionBuilder();
+        secondServiceFunctionBuilder.setName(secondFunctionName)
+                .setKey(new ServiceFunctionKey(secondFunctionName));
+        // Third SF
+        ServiceFunctionBuilder thirdServiceFunctionBuilder = new ServiceFunctionBuilder();
+        thirdServiceFunctionBuilder.setName(thirdFunctionName)
+                .setKey(new ServiceFunctionKey(thirdFunctionName));
+
+        SfcProviderServiceForwarderAPI.putServiceFunctionForwarder(serviceForwarderBuilder.build());
+
+        new IosXeDataStoreAPI(dataBroker, firstServiceFunctionBuilder.build(), WRITE_FUNCTION, OPERATIONAL).call();
+        new IosXeDataStoreAPI(dataBroker, secondServiceFunctionBuilder.build(), WRITE_FUNCTION, OPERATIONAL).call();
+        new IosXeDataStoreAPI(dataBroker, thirdServiceFunctionBuilder.build(), WRITE_FUNCTION, OPERATIONAL).call();
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapperTest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceForwarderMapperTest.java
new file mode 100644 (file)
index 0000000..8356442
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class IosXeServiceForwarderMapperTest {
+
+    private DataBroker dataBroker;
+    private NodeManager nodeManager;
+    private IosXeServiceForwarderMapper sffMapper;
+    private final String nodeId = "nodeId";
+    private final String sffName2 = "forwarder2";
+    private final String ipAddress = "10.0.0.1";
+
+    @Before
+    public void init() {
+        dataBroker = mock(DataBroker.class);
+        nodeManager = mock(NodeManager.class);
+    }
+
+    @Test
+    public void syncForwarder_create() {
+        // Forwarders
+        List<ServiceFunctionForwarder> forwarders = new ArrayList<>();
+        // SFF without management ip
+        ServiceFunctionForwarderBuilder noMgmtIpForwarder = new ServiceFunctionForwarderBuilder();
+        String sffName1 = "forwarder1";
+        noMgmtIpForwarder.setName(new SffName(sffName1))
+                .setKey(new ServiceFunctionForwarderKey(new SffName(sffName1)));
+        // Test SFF
+        List<SffDataPlaneLocator> dataPlaneLocators = new ArrayList<>();
+        DataPlaneLocatorBuilder dataPlaneLocatorBuilder = new DataPlaneLocatorBuilder();
+        dataPlaneLocatorBuilder.setLocatorType(new IpBuilder().setIp(new IpAddress(new Ipv4Address(ipAddress)))
+                .build());
+        SffDataPlaneLocatorBuilder sffDataPlaneLocatorBuilder = new SffDataPlaneLocatorBuilder();
+        sffDataPlaneLocatorBuilder.setDataPlaneLocator(dataPlaneLocatorBuilder.build());
+        dataPlaneLocators.add(sffDataPlaneLocatorBuilder.build());
+
+        ServiceFunctionForwarderBuilder testSff = new ServiceFunctionForwarderBuilder();
+        testSff.setName(new SffName(sffName2))
+                .setKey(new ServiceFunctionForwarderKey(new SffName(sffName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)))
+                .setSffDataPlaneLocator(dataPlaneLocators);
+        forwarders.add(noMgmtIpForwarder.build());
+        forwarders.add(testSff.build());
+        // Node
+        Map<NodeId, Node> nodeMap = new HashMap<>();
+        Map<NodeId, DataBroker> nodeDbMap = new HashMap<>();
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setNodeId(new NodeId(nodeId))
+                .setKey(new NodeKey(new NodeId(nodeId)));
+        Node node = nodeBuilder.build();
+        nodeMap.put(node.getNodeId(), node);
+        nodeDbMap.put(node.getNodeId(), dataBroker);
+
+        when(nodeManager.getConnectedNodes()).thenReturn(nodeMap);
+        when(nodeManager.getNetconfNodeIp(node)).thenReturn(new IpAddress(new Ipv4Address(ipAddress)));
+        when(nodeManager.getActiveMountPoints()).thenReturn(nodeDbMap);
+
+        sffMapper = new IosXeServiceForwarderMapper(dataBroker, nodeManager);
+        sffMapper.syncForwarders(forwarders, false);
+
+        verify(nodeManager, times(1)).getConnectedNodes();
+        verify(nodeManager,times(1)).getNetconfNodeIp(node);
+        verify(nodeManager, times(1)).getActiveMountPoints();
+        verify(dataBroker, times(1)).newWriteOnlyTransaction();
+    }
+
+    @Test
+    public void syncForwarder_delete() {
+        // Forwarders
+        List<ServiceFunctionForwarder> forwarders = new ArrayList<>();
+        // Test SFF
+        List<SffDataPlaneLocator> dataPlaneLocators = new ArrayList<>();
+        DataPlaneLocatorBuilder dataPlaneLocatorBuilder = new DataPlaneLocatorBuilder();
+        dataPlaneLocatorBuilder.setLocatorType(new IpBuilder().setIp(new IpAddress(new Ipv4Address(ipAddress)))
+                .build());
+        SffDataPlaneLocatorBuilder sffDataPlaneLocatorBuilder = new SffDataPlaneLocatorBuilder();
+        sffDataPlaneLocatorBuilder.setDataPlaneLocator(dataPlaneLocatorBuilder.build());
+        dataPlaneLocators.add(sffDataPlaneLocatorBuilder.build());
+
+        ServiceFunctionForwarderBuilder testSff = new ServiceFunctionForwarderBuilder();
+        testSff.setName(new SffName(sffName2))
+                .setKey(new ServiceFunctionForwarderKey(new SffName(sffName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)))
+                .setSffDataPlaneLocator(dataPlaneLocators);
+        forwarders.add(testSff.build());
+        // Node
+        Map<NodeId, Node> nodeMap = new HashMap<>();
+        Map<NodeId, DataBroker> nodeDbMap = new HashMap<>();
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setNodeId(new NodeId(nodeId))
+                .setKey(new NodeKey(new NodeId(nodeId)));
+        Node node = nodeBuilder.build();
+        nodeMap.put(node.getNodeId(), node);
+        nodeDbMap.put(node.getNodeId(), dataBroker);
+
+        when(nodeManager.getConnectedNodes()).thenReturn(nodeMap);
+        when(nodeManager.getNetconfNodeIp(node)).thenReturn(new IpAddress(new Ipv4Address(ipAddress)));
+        when(nodeManager.getActiveMountPoints()).thenReturn(nodeDbMap);
+
+        sffMapper = new IosXeServiceForwarderMapper(dataBroker, nodeManager);
+        sffMapper.syncForwarders(forwarders, true);
+
+        verify(nodeManager, times(1)).getConnectedNodes();
+        verify(nodeManager,times(1)).getNetconfNodeIp(node);
+        verify(nodeManager, times(1)).getActiveMountPoints();
+        verify(dataBroker, times(1)).newWriteOnlyTransaction();
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapperTest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/IosXeServiceFunctionMapperTest.java
new file mode 100644 (file)
index 0000000..e5665b8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.functions.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.Gre;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.MacBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class IosXeServiceFunctionMapperTest {
+
+    private final String nodeIdString = "nodeId";
+    private final String ipAddress = "10.0.0.1";
+    private DataBroker dataBroker;
+    private NodeManager nodeManager;
+    private IosXeServiceFunctionMapper sfMapper;
+
+    @Before
+    public void init() {
+        dataBroker = mock(DataBroker.class);
+        nodeManager = mock(NodeManager.class);
+    }
+
+    @Test
+    public void syncFunctions_update() {
+        String sfName1 = "function1";
+        String sfName2 = "function2";
+        // Prepare DPL
+        SfDataPlaneLocatorBuilder macLocatorType = new SfDataPlaneLocatorBuilder();
+        macLocatorType.setLocatorType(new MacBuilder().build());
+        SfDataPlaneLocatorBuilder ipLocatorType = new SfDataPlaneLocatorBuilder();
+        ipLocatorType.setLocatorType(new IpBuilder().setIp(new IpAddress(new Ipv4Address(ipAddress))).build())
+                .setTransport(Gre.class);
+        SfDataPlaneLocator macDpl = macLocatorType.build();
+        SfDataPlaneLocator ipDpl = ipLocatorType.build();
+        List<SfDataPlaneLocator> dataPlaneLocatorList;
+        // Service functions
+        List<ServiceFunction> serviceFunctions = new ArrayList<>();
+        // SF without management IP
+        ServiceFunctionBuilder emptySfBuilder = new ServiceFunctionBuilder();
+        emptySfBuilder.setName(new SfName(sfName1))
+                .setKey(new ServiceFunctionKey(new SfName(sfName1)));
+        // SF without data plane locator
+        ServiceFunctionBuilder noDplSfBuilder = new ServiceFunctionBuilder();
+        noDplSfBuilder.setName(new SfName(sfName2))
+                .setKey(new ServiceFunctionKey(new SfName(sfName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)));
+        // SF without ip data plane locator
+        ServiceFunctionBuilder noIpDplSfBuilder = new ServiceFunctionBuilder();
+        dataPlaneLocatorList = new ArrayList<>();
+        dataPlaneLocatorList.add(macDpl);
+        noIpDplSfBuilder.setName(new SfName(sfName2))
+                .setKey(new ServiceFunctionKey(new SfName(sfName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)))
+                .setSfDataPlaneLocator(dataPlaneLocatorList);
+        // Test SF
+        ServiceFunctionBuilder testSfBuilder = new ServiceFunctionBuilder();
+        dataPlaneLocatorList = new ArrayList<>();
+        dataPlaneLocatorList.add(ipDpl);
+        testSfBuilder.setName(new SfName(sfName2))
+                .setKey(new ServiceFunctionKey(new SfName(sfName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)))
+                .setSfDataPlaneLocator(dataPlaneLocatorList);
+        serviceFunctions.add(emptySfBuilder.build());
+        serviceFunctions.add(noDplSfBuilder.build());
+        serviceFunctions.add(noIpDplSfBuilder.build());
+        serviceFunctions.add(testSfBuilder.build());
+        // Node list
+        Map<NodeId, Node> nodeMap = new HashMap<>();
+        NodeId nodeId = new NodeId(nodeIdString);
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setHost(new Host(new IpAddress(new Ipv4Address(ipAddress))));
+        nodeBuilder.setNodeId(nodeId)
+                .addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+        Node node = nodeBuilder.build();
+        nodeMap.put(nodeId, node);
+
+        Map<NodeId, DataBroker> nodeWithDataBrokerMap = new HashMap<>();
+        nodeWithDataBrokerMap.put(nodeId, dataBroker);
+
+        when(nodeManager.getConnectedNodes()).thenReturn(nodeMap);
+        when(nodeManager.getNetconfNodeIp(node)).thenReturn(new IpAddress(new Ipv4Address(ipAddress)));
+        when(nodeManager.getActiveMountPoints()).thenReturn(nodeWithDataBrokerMap);
+
+        sfMapper = new IosXeServiceFunctionMapper(dataBroker, nodeManager);
+        sfMapper.syncFunctions(serviceFunctions, false);
+
+        verify(nodeManager, times(3)).getConnectedNodes();
+        verify(nodeManager, times(3)).getNetconfNodeIp(any(Node.class));
+        verify(nodeManager, times(3)).getActiveMountPoints();
+        verify(dataBroker, times(1)).newWriteOnlyTransaction();
+    }
+
+    @Test
+    public void syncFunctions_delete() {
+        String sfName2 = "function2";
+        sfMapper = new IosXeServiceFunctionMapper(dataBroker, nodeManager);
+        // Prepare DPL
+        SfDataPlaneLocatorBuilder macLocatorType = new SfDataPlaneLocatorBuilder();
+        macLocatorType.setLocatorType(new MacBuilder().build());
+        SfDataPlaneLocatorBuilder ipLocatorType = new SfDataPlaneLocatorBuilder();
+        ipLocatorType.setLocatorType(new IpBuilder().setIp(new IpAddress(new Ipv4Address(ipAddress))).build())
+                .setTransport(Gre.class);
+        SfDataPlaneLocator ipDpl = ipLocatorType.build();
+        List<SfDataPlaneLocator> dataPlaneLocatorList;
+        // Service function
+        List<ServiceFunction> serviceFunctions = new ArrayList<>();
+        ServiceFunctionBuilder testSfBuilder = new ServiceFunctionBuilder();
+        dataPlaneLocatorList = new ArrayList<>();
+        dataPlaneLocatorList.add(ipDpl);
+        testSfBuilder.setName(new SfName(sfName2))
+                .setKey(new ServiceFunctionKey(new SfName(sfName2)))
+                .setIpMgmtAddress(new IpAddress(new Ipv4Address(ipAddress)))
+                .setSfDataPlaneLocator(dataPlaneLocatorList);
+        serviceFunctions.add(testSfBuilder.build());
+        // Node list
+        Map<NodeId, Node> nodeMap = new HashMap<>();
+        NodeId nodeId = new NodeId(nodeIdString);
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setHost(new Host(new IpAddress(new Ipv4Address(ipAddress))));
+        nodeBuilder.setNodeId(nodeId)
+                .addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+        Node node = nodeBuilder.build();
+        nodeMap.put(nodeId, node);
+
+        Map<NodeId, DataBroker> nodeWithDataBrokerMap = new HashMap<>();
+        nodeWithDataBrokerMap.put(nodeId, dataBroker);
+
+        when(nodeManager.getConnectedNodes()).thenReturn(nodeMap);
+        when(nodeManager.getNetconfNodeIp(node)).thenReturn(new IpAddress(new Ipv4Address(ipAddress)));
+        when(nodeManager.getActiveMountPoints()).thenReturn(nodeWithDataBrokerMap);
+
+        sfMapper.syncFunctions(serviceFunctions, true);
+
+        verify(nodeManager, times(1)).getConnectedNodes();
+        verify(nodeManager, times(1)).getNetconfNodeIp(any(Node.class));
+        verify(nodeManager, times(1)).getActiveMountPoints();
+        verify(dataBroker, times(1)).newWriteOnlyTransaction();
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManagerTest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/renderer/NodeManagerTest.java
new file mode 100644 (file)
index 0000000..032fe46
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.renderer;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+
+public class NodeManagerTest {
+
+    private final String nodeId = "nodeId";
+    private final String topologyId = "topologyId";
+    private NodeManager manager;
+    private MountPoint mountPoint;
+    private DataBroker dataBroker;
+    private BindingAwareBroker bindingAwareBroker;
+    private BindingAwareBroker.ProviderContext providerContext;
+    private MountPointService mountPointService;
+    // Optionals
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    private Optional<MountPoint> optionalMountPointObject;
+    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+    private Optional<DataBroker> optionalDataBrokerObject;
+
+    @Before
+    @SuppressWarnings("unchecked")
+    public void init() {
+        mountPoint = mock(MountPoint.class);
+        dataBroker = mock(DataBroker.class);
+        bindingAwareBroker = mock(BindingAwareBroker.class);
+        providerContext = mock(BindingAwareBroker.ProviderContext.class);
+        mountPointService = mock(MountPointService.class);
+        // Optionals
+        optionalMountPointObject = mock(Optional.class);
+        optionalDataBrokerObject = mock(Optional.class);
+    }
+
+    @Test
+    public void updateNode_unsuccessful() {
+        // Prepare topology
+        TopologyBuilder topologyBuilder = new TopologyBuilder();
+        topologyBuilder.setTopologyId(new TopologyId(topologyId));
+        // Prepare node
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected);
+        nodeBuilder.setNodeId(new NodeId(nodeId));
+        nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+
+        when(bindingAwareBroker.registerProvider(any(BindingAwareProvider.class))).thenReturn(providerContext);
+        when(providerContext.getSALService(any())).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(optionalMountPointObject);
+
+        manager = new NodeManager(dataBroker, bindingAwareBroker);
+        manager.updateNode(nodeBuilder.build());
+
+        assertTrue(manager.getActiveMountPoints().isEmpty());
+        assertTrue(manager.getConnectedNodes().isEmpty());
+    }
+
+    @Test
+    public void updateNode_successful() {
+        // Prepare topology
+        TopologyBuilder topologyBuilder = new TopologyBuilder();
+        topologyBuilder.setTopologyId(new TopologyId(topologyId));
+        // Prepare node
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected);
+        nodeBuilder.setNodeId(new NodeId(nodeId));
+        nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+
+        when(bindingAwareBroker.registerProvider(any(BindingAwareProvider.class))).thenReturn(providerContext);
+        when(providerContext.getSALService(any())).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(optionalMountPointObject);
+        when(mountPoint.getService(eq(DataBroker.class))).thenReturn(optionalDataBrokerObject);
+
+        // Mock getting mountpoint
+        when(optionalMountPointObject.isPresent()).thenReturn(true);
+        //noinspection OptionalGetWithoutIsPresent
+        when(optionalMountPointObject.get()).thenReturn(mountPoint);
+        when(optionalDataBrokerObject.isPresent()).thenReturn(true);
+        //noinspection OptionalGetWithoutIsPresent
+        when(optionalDataBrokerObject.get()).thenReturn(dataBroker);
+
+        manager = new NodeManager(dataBroker, bindingAwareBroker);
+        manager.updateNode(nodeBuilder.build());
+
+        assertFalse(manager.getActiveMountPoints().isEmpty());
+        assertFalse(manager.getConnectedNodes().isEmpty());
+    }
+
+    @Test
+    public void updateAndRemoveNode() {
+        // Prepare topology
+        TopologyBuilder topologyBuilder = new TopologyBuilder();
+        topologyBuilder.setTopologyId(new TopologyId(topologyId));
+        // Prepare node
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder();
+        netconfNodeBuilder.setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected);
+        nodeBuilder.setNodeId(new NodeId(nodeId));
+        nodeBuilder.addAugmentation(NetconfNode.class, netconfNodeBuilder.build());
+        Node testNode = nodeBuilder.build();
+
+        when(bindingAwareBroker.registerProvider(any(BindingAwareProvider.class))).thenReturn(providerContext);
+        when(providerContext.getSALService(any())).thenReturn(mountPointService);
+        when(mountPointService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(optionalMountPointObject);
+        when(mountPoint.getService(eq(DataBroker.class))).thenReturn(optionalDataBrokerObject);
+
+        // Mock getting mountpoint
+        when(optionalMountPointObject.isPresent()).thenReturn(true);
+        //noinspection OptionalGetWithoutIsPresent
+        when(optionalMountPointObject.get()).thenReturn(mountPoint);
+        when(optionalDataBrokerObject.isPresent()).thenReturn(true);
+        //noinspection OptionalGetWithoutIsPresent
+        when(optionalDataBrokerObject.get()).thenReturn(dataBroker);
+
+        manager = new NodeManager(dataBroker, bindingAwareBroker);
+        manager.updateNode(testNode);
+
+        assertTrue(manager.getActiveMountPoints().size() == 1);
+        assertTrue(manager.getConnectedNodes().size() == 1);
+
+        manager.removeNode(testNode);
+
+        assertTrue(manager.getActiveMountPoints().isEmpty());
+        assertTrue(manager.getConnectedNodes().isEmpty());
+    }
+}
\ No newline at end of file
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPITest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/IosXeDataStoreAPITest.java
new file mode 100644 (file)
index 0000000..6c2e697
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.utils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.sfc.provider.OpendaylightSfc;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.ConfigServiceChainSfModeBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.EncapsulationBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.config.service.chain.sf.mode.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.LocalBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.ConfigServiceChainPathModeBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.path.config.service.chain.path.mode.ServiceIndexBuilder;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_FUNCTION;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_LOCAL;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_PATH;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.DELETE_REMOTE;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.READ_FUNCTION;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.READ_LOCAL;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.READ_REMOTE;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_FUNCTION;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_LOCAL;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_PATH;
+import static org.opendaylight.sfc.sfc_ios_xe.provider.utils.IosXeDataStoreAPI.Transaction.WRITE_REMOTE;
+
+public class IosXeDataStoreAPITest extends AbstractDataBrokerTest {
+
+    private final OpendaylightSfc odl = new OpendaylightSfc();
+    private final String REMOTE_FORWARDER = "remote-forwarder";
+    private final String SERVICE_NAME = "service-function";
+    private IosXeDataStoreAPI iosXeDataStoreAPI;
+    private DataBroker mountpoint;
+
+    @Before
+    public void init() {
+        // Initialize datastore
+        mountpoint = getDataBroker();
+        odl.setDataProvider(mountpoint);
+    }
+
+    @Test
+    public void writeServiceFunction() {
+        ServiceFunction data = buildTestServiceFunction();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_FUNCTION, LogicalDatastoreType.CONFIGURATION);
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+    }
+
+    @Test
+    public void readServiceFunction() {
+        ServiceFunction data = buildTestServiceFunction();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SfName(SERVICE_NAME), READ_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read empty datastore
+        ServiceFunction function = (ServiceFunction) iosXeDataStoreAPI.call();
+        assertNull(function);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Write service function
+        Boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SfName(SERVICE_NAME), READ_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read again
+        function = (ServiceFunction) iosXeDataStoreAPI.call();
+        assertEquals(buildTestServiceFunction(), function);
+    }
+
+    @Test
+    public void deleteServiceFunction() {
+        ServiceFunction data = buildTestServiceFunction();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Put service function
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SfName(SERVICE_NAME), READ_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read it
+        ServiceFunction function = (ServiceFunction) iosXeDataStoreAPI.call();
+        assertEquals(buildTestServiceFunction(), function);
+        // Remove
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new ServiceFunctionKey(SERVICE_NAME), DELETE_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SfName(SERVICE_NAME), READ_FUNCTION,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read again, should be null
+        function = (ServiceFunction) iosXeDataStoreAPI.call();
+        assertNull(function);
+    }
+
+    @Test
+    public void writeLocalServiceForwarder() {
+        Local data = buildLocalServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_LOCAL, LogicalDatastoreType.CONFIGURATION);
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+    }
+
+    @Test
+    public void readLocalServiceForwarder() {
+        Local data = buildLocalServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, null, READ_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        Local forwarder = (Local) iosXeDataStoreAPI.call();
+        assertNull(forwarder);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        Boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, null, READ_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        forwarder = (Local) iosXeDataStoreAPI.call();
+        assertEquals(buildLocalServiceForwarder(), forwarder);
+    }
+
+    @Test
+    public void deleteLocalServiceForwarder() {
+        Local data = buildLocalServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        // Put service function
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, null, READ_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read it
+        Local forwarder = (Local) iosXeDataStoreAPI.call();
+        assertEquals(buildLocalServiceForwarder(), forwarder);
+        // Remove
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, null, DELETE_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, null, READ_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read again, should be null
+        forwarder = (Local) iosXeDataStoreAPI.call();
+        assertNull(forwarder);
+    }
+
+    @Test
+    public void writeRemoteServiceForwarder() {
+        ServiceFfName data = buildRemoteServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_REMOTE, LogicalDatastoreType.CONFIGURATION);
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+    }
+
+    @Test
+    public void readRemoteServiceForwarder() {
+        ServiceFfName data = buildRemoteServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SffName(REMOTE_FORWARDER), READ_REMOTE,
+                LogicalDatastoreType.CONFIGURATION);
+        ServiceFfName forwarder = (ServiceFfName) iosXeDataStoreAPI.call();
+        assertNull(forwarder);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_REMOTE, LogicalDatastoreType.CONFIGURATION);
+        Boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SffName(REMOTE_FORWARDER), READ_REMOTE,
+                LogicalDatastoreType.CONFIGURATION);
+        forwarder = (ServiceFfName) iosXeDataStoreAPI.call();
+        assertEquals(buildRemoteServiceForwarder(), forwarder);
+    }
+
+    @Test
+    public void deleteRemoteServiceForwarder() {
+        ServiceFfName data = buildRemoteServiceForwarder();
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_REMOTE,
+                LogicalDatastoreType.CONFIGURATION);
+        // Put service function
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SffName(REMOTE_FORWARDER), READ_REMOTE,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read it
+        ServiceFfName forwarder = (ServiceFfName) iosXeDataStoreAPI.call();
+        assertEquals(buildRemoteServiceForwarder(), forwarder);
+        // Remove
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, DELETE_REMOTE, LogicalDatastoreType.CONFIGURATION);
+        result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new SffName(REMOTE_FORWARDER), READ_LOCAL,
+                LogicalDatastoreType.CONFIGURATION);
+        // Read again, should be null
+        forwarder = (ServiceFfName) iosXeDataStoreAPI.call();
+        assertNull(forwarder);
+    }
+
+    @Test
+    public void writeRemovePath() {
+        ServicePath data = buildServicePath();
+        // Write
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, data, WRITE_PATH, LogicalDatastoreType.CONFIGURATION);
+        boolean result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+        // Delete
+        iosXeDataStoreAPI = new IosXeDataStoreAPI(mountpoint, new ServicePathKey(1L), DELETE_PATH,
+                LogicalDatastoreType.CONFIGURATION);
+        result = (boolean) iosXeDataStoreAPI.call();
+        assertTrue(result);
+    }
+
+    private ServiceFunction buildTestServiceFunction() {
+        ConfigServiceChainSfModeBuilder sfModeBuilder = new ConfigServiceChainSfModeBuilder();
+        sfModeBuilder.setIp(new IpBuilder().setAddress(new Ipv4Address("10.0.0.1")).build())
+                .setEncapsulation(new EncapsulationBuilder().setNone(true).build());
+        ServiceFunctionBuilder serviceFunctionBuilder = new ServiceFunctionBuilder();
+        serviceFunctionBuilder.setName(SERVICE_NAME)
+                .setKey(new ServiceFunctionKey(SERVICE_NAME))
+                .setConfigServiceChainSfMode(sfModeBuilder.build());
+        return serviceFunctionBuilder.build();
+    }
+
+    private Local buildLocalServiceForwarder() {
+        LocalBuilder localBuilder = new LocalBuilder();
+        localBuilder.setIp(new org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder()
+                .setAddress(new Ipv4Address("100.0.0.1")).build());
+        return localBuilder.build();
+    }
+
+    private ServiceFfName buildRemoteServiceForwarder() {
+        ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder();
+        serviceFfNameBuilder.setName(REMOTE_FORWARDER)
+                .setKey(new ServiceFfNameKey(REMOTE_FORWARDER))
+                .setIp(new org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.config.service.chain.grouping.IpBuilder()
+                        .setAddress(new Ipv4Address("200.0.0.1")).build());
+        return serviceFfNameBuilder.build();
+    }
+
+    private ServicePath buildServicePath() {
+        ConfigServiceChainPathModeBuilder configServiceChainPathModeBuilder = new ConfigServiceChainPathModeBuilder();
+        configServiceChainPathModeBuilder.setServiceIndex(new ServiceIndexBuilder().build());
+        ServicePathBuilder servicePathBuilder = new ServicePathBuilder();
+        servicePathBuilder.setKey(new ServicePathKey(1L))
+                .setServicePathId(1L)
+                .setConfigServiceChainPathMode(configServiceChainPathModeBuilder.build());
+        return servicePathBuilder.build();
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtilsTest.java b/sfc-renderers/sfc-ios-xe-renderer/src/test/java/org/opendaylight/sfc/sfc_ios_xe/provider/utils/SfcIosXeUtilsTest.java
new file mode 100644 (file)
index 0000000..858436c
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.sfc.sfc_ios_xe.provider.utils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocator;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.SffDataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarder.base.sff.data.plane.locator.DataPlaneLocatorBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarderBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.IpBuilder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sl.rev140701.data.plane.locator.locator.type.MacBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308.Native;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.ServiceChain;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunction;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePath;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServicePathKey;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.Local;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfName;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameBuilder;
+import org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.service.function.forwarder.ServiceFfNameKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({SfcProviderServiceForwarderAPI.class})
+public class SfcIosXeUtilsTest {
+
+    private final String ipv4Address = "10.0.0.1";
+    private final String forwarderName = "forwarder";
+
+    @Test
+    public void createLocalForwarder_nullIp() {
+        assertNull(SfcIosXeUtils.createLocalForwarder(null));
+    }
+
+    @Test
+    public void createLocalForwarder() {
+        IpAddress ipAddress = new IpAddress(new Ipv4Address(ipv4Address));
+        ServiceFunctionForwarder result = SfcIosXeUtils.createLocalForwarder(ipAddress);
+        assertNotNull(result);
+        assertEquals(result.getLocal().getIp().getAddress().getValue(), ipv4Address);
+    }
+
+    @Test
+    public void createRemoteForwarder_noForwarderFound() {
+        SffName forwarderSff = new SffName(forwarderName);
+
+        PowerMockito.stub(PowerMockito.method(SfcProviderServiceForwarderAPI.class, "readServiceFunctionForwarder"))
+                .toReturn(null);
+
+        ServiceFfName result = SfcIosXeUtils.createRemoteForwarder(forwarderSff);
+        assertNull(result);
+    }
+
+    @Test
+    public void createRemoteForwarder_noIpLocatorType() {
+        ServiceFunctionForwarderBuilder serviceFunctionForwarderBuilder = new ServiceFunctionForwarderBuilder();
+        List<SffDataPlaneLocator> sffDataPlaneLocators = new ArrayList<>();
+        SffDataPlaneLocatorBuilder sffDataPlaneLocatorBuilder = new SffDataPlaneLocatorBuilder();
+        DataPlaneLocatorBuilder dataPlaneLocatorBuilder = new DataPlaneLocatorBuilder();
+        MacBuilder macBuilder = new MacBuilder();
+        dataPlaneLocatorBuilder.setLocatorType(macBuilder.build());
+        sffDataPlaneLocatorBuilder.setDataPlaneLocator(dataPlaneLocatorBuilder.build());
+        sffDataPlaneLocators.add(sffDataPlaneLocatorBuilder.build());
+        serviceFunctionForwarderBuilder.setSffDataPlaneLocator(sffDataPlaneLocators);
+
+        SffName forwarderSff = new SffName(forwarderName);
+
+        PowerMockito.stub(PowerMockito.method(SfcProviderServiceForwarderAPI.class, "readServiceFunctionForwarder"))
+                .toReturn(serviceFunctionForwarderBuilder.build());
+
+        ServiceFfName result = SfcIosXeUtils.createRemoteForwarder(forwarderSff);
+        assertNull(result);
+    }
+
+    @Test
+    public void createRemoteForwarder() {
+        ServiceFunctionForwarderBuilder serviceFunctionForwarderBuilder = new ServiceFunctionForwarderBuilder();
+        List<SffDataPlaneLocator> sffDataPlaneLocators = new ArrayList<>();
+        SffDataPlaneLocatorBuilder sffDataPlaneLocatorBuilder = new SffDataPlaneLocatorBuilder();
+        DataPlaneLocatorBuilder dataPlaneLocatorBuilder = new DataPlaneLocatorBuilder();
+        IpBuilder ipBuilder = new IpBuilder();
+        ipBuilder.setIp(new IpAddress(new Ipv4Address(ipv4Address)))
+                .setPort(new PortNumber(100));
+        dataPlaneLocatorBuilder.setLocatorType(ipBuilder.build());
+        sffDataPlaneLocatorBuilder.setDataPlaneLocator(dataPlaneLocatorBuilder.build());
+        sffDataPlaneLocators.add(sffDataPlaneLocatorBuilder.build());
+        serviceFunctionForwarderBuilder.setSffDataPlaneLocator(sffDataPlaneLocators);
+
+        SffName forwarderSff = new SffName(forwarderName);
+
+        PowerMockito.stub(PowerMockito.method(SfcProviderServiceForwarderAPI.class, "readServiceFunctionForwarder"))
+                .toReturn(serviceFunctionForwarderBuilder.build());
+
+        ServiceFfName result = SfcIosXeUtils.createRemoteForwarder(forwarderSff);
+        assertNotNull(result);
+        assertEquals(result.getIp().getAddress().getValue(), ipv4Address);
+    }
+
+    @Test
+    public void getDplWithIpLocatorType_noIpLocator() {
+        List<SfDataPlaneLocator> dataPlaneLocators = new ArrayList<>();
+        org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder macLocatorTypeBuilder =
+                new org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder();
+        macLocatorTypeBuilder.setLocatorType(new MacBuilder().build());
+        dataPlaneLocators.add(macLocatorTypeBuilder.build());
+        SfDataPlaneLocator result = SfcIosXeUtils.getDplWithIpLocatorType(dataPlaneLocators);
+        assertNull(result);
+    }
+
+    @Test
+    public void getDplWithIpLocatorType() {
+        List<SfDataPlaneLocator> dataPlaneLocators = new ArrayList<>();
+        org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder macLocatorTypeBuilder =
+                new org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder();
+        macLocatorTypeBuilder.setLocatorType(new MacBuilder().build());
+        SfDataPlaneLocator macLocatorDpl = macLocatorTypeBuilder.build();
+        org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder ipLocatorTypeBuilder =
+                new org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sf.rev140701.service.function.base.SfDataPlaneLocatorBuilder();
+        ipLocatorTypeBuilder.setLocatorType(new IpBuilder().build());
+        SfDataPlaneLocator ipLocatorDpl = ipLocatorTypeBuilder.build();
+        dataPlaneLocators.add(macLocatorDpl);
+        dataPlaneLocators.add(ipLocatorDpl);
+        SfDataPlaneLocator result = SfcIosXeUtils.getDplWithIpLocatorType(dataPlaneLocators);
+        assertNotNull(result);
+    }
+
+    @Test
+    public void createLocalSffIid() {
+        InstanceIdentifier<Local> result = SfcIosXeUtils.createLocalSffIid();
+        // Test IID
+        InstanceIdentifier<Local> testIid = InstanceIdentifier.builder(Native.class).child(ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(Local.class).build();
+        assertEquals(testIid, result);
+    }
+
+    @Test
+    public void createRemoteSffIid() {
+        ServiceFfNameBuilder serviceFfNameBuilder = new ServiceFfNameBuilder();
+        serviceFfNameBuilder.setName(forwarderName);
+        InstanceIdentifier<ServiceFfName> firstResult = SfcIosXeUtils.createRemoteSffIid(serviceFfNameBuilder.build());
+        InstanceIdentifier<ServiceFfName> secondResult = SfcIosXeUtils.createRemoteSffIid(new SffName(forwarderName));
+        // Test IID
+        InstanceIdentifier<ServiceFfName> testIid = InstanceIdentifier.builder(Native.class).child(ServiceChain.class)
+                .child(org.opendaylight.yang.gen.v1.urn.ios.rev160308._native.service.chain.ServiceFunctionForwarder.class)
+                .child(ServiceFfName.class, new ServiceFfNameKey(forwarderName)).build();
+        assertEquals(firstResult, testIid);
+        assertEquals(secondResult, testIid);
+        assertEquals(firstResult, secondResult);
+    }
+
+    @Test
+    public void createSfIid() {
+        String functionName = "function";
+        InstanceIdentifier<ServiceFunction> result = SfcIosXeUtils.createSfIid(new ServiceFunctionKey(functionName));
+        // Test IID
+        InstanceIdentifier<ServiceFunction> testIid = InstanceIdentifier.builder(Native.class)
+                .child(ServiceChain.class)
+                .child(ServiceFunction.class, new ServiceFunctionKey(functionName))
+                .build();
+        assertEquals(testIid, result);
+    }
+
+    @Test
+    public void createServicePathIid() {
+        Long servicePathKey = 10L;
+        InstanceIdentifier<ServicePath> result = SfcIosXeUtils.createServicePathIid(new ServicePathKey(servicePathKey));
+        // Test IID
+        InstanceIdentifier<ServicePath> testIid = InstanceIdentifier.builder(Native.class).child(ServiceChain.class)
+                .child(ServicePath.class, new ServicePathKey(servicePathKey)).build();
+        assertEquals(testIid, result);
+    }
+}
\ No newline at end of file