<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>
<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>
<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>
--- /dev/null
+<?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
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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 {
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+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");
+ }
+
+}
--- /dev/null
+<?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&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:sfc-provider:impl?module=sfc-provider-impl&revision=2014-06-30</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:sfc-ios-xe:impl?module=sfc-ios-xe-impl&revision=2014-10-20</capability>
+ </required-capabilities>
+
+</snapshot>
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * 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