import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
}
/**
- * Match Source IP Address.
+ * Match the set of these vlans with that of flowSpec's vlans.
*
- * @param flowSpec Flow Specification
+ * @param flowSpec
+ * Flow Specification
* @return true, if successful
*/
private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
if (dlVlan == null || flowSpec.dlVlan == null) {
return false;
}
- return dlVlan.equals(flowSpec.dlVlan);
+
+ return this.getVlanList().equals(flowSpec.getVlanList());
}
/**
}
/**
- * Returns the vlan id number
+ * Returns the vlan id number for all vlans specified
*
- * @return the vlan id number
+ * @return the vlan id number for all vlans specified
*/
- public Short getVlanId() {
- Short vlan = 0;
+ public Set<Short> getVlanList() {
+ /*
+ * example: Vlan = "1,3,5-12"
+ * elemArray = ["1" "3" "5-12"]
+ * elem[2] = "5-12" --> limits = ["5" "12"]
+ * vlanList = [1 3 5 6 7 8 9 10 11 12]
+ */
+ Set<Short> vlanList = new HashSet<Short>();
try {
- vlan = Short.parseShort(dlVlan);
+ String[] elemArray = dlVlan.split(",");
+ for (String elem : elemArray) {
+ if (elem.contains("-")) {
+ String[] limits = elem.split("-");
+ for (short j = Short.valueOf(limits[0]); j <= Short.valueOf(limits[1]); j++) {
+ vlanList.add(Short.valueOf(j));
+ }
+ } else {
+ vlanList.add(Short.valueOf(elem));
+ }
+ }
} catch (NumberFormatException e) {
}
- return vlan;
+ return vlanList;
}
/**
if (dlVlan != null) {
short vlanId = 0;
try {
- vlanId = Short.parseShort(dlVlan);
+ String[] elemArray = dlVlan.split(",");
+ for (String elem : elemArray) {
+ if (elem.contains("-")) {
+ String[] limits = elem.split("-");
+ if (Short.parseShort(limits[0]) < 0
+ || Short.parseShort(limits[0]) >= Short.parseShort(limits[1])
+ || Short.parseShort(limits[1]) > 0xfff) {
+ return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
+ }
+ } else {
+ vlanId = Short.parseShort(elem);
+ if (vlanId < 0 || vlanId > 0xfff) {
+ return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
+ }
+ }
+ }
} catch (NumberFormatException e) {
return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
}
- if (vlanId < 0 || vlanId > 0xfff) {
- return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
- }
}
return new Status(StatusCode.SUCCESS);
}
/**
* Returns the matches.
- * If unidirectional flag is set, there will be only one match in the list
- * If unidirectional flag is unset there will be two matches in the list,
+ * If unidirectional flag is set, there will be only one match per vlan in the list
+ * If unidirectional flag is unset there will be two matches per vlan in the list,
* only if the specified flow has an intrinsic direction.
* For Ex. if the cFlow only has the protocol field configured, no matter
- * if unidirectional flag is set or not, only one match will be returned
+ * if unidirectional flag is set or not, only one match per vlan will be returned
* The client just has to iterate over the returned list
* @return the matches
*/
public List<Match> getMatches() {
List<Match> matches = new ArrayList<Match>();
- Match match = new Match();
if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
- match.setField(MatchType.DL_VLAN, this.getVlanId());
+ for(Short vlan:getVlanList()){
+ Match match = getMatch(vlan);
+ matches.add(match);
+ }
+ }
+ else{
+ Match match = getMatch(null);
+ matches.add(match);
+ }
+
+ if (!ContainerFlowConfig.unidirectional) {
+ List<Match> forwardMatches = new ArrayList<Match>(matches);
+ for (Match match : forwardMatches) {
+ Match reverse = match.reverse();
+ if (!match.equals(reverse)) {
+ matches.add(reverse);
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ private Match getMatch(Short vlan){
+ Match match = new Match();
+
+ if (vlan != null) {
+ match.setField(MatchType.DL_VLAN, vlan);
}
if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
String parts[] = this.nwSrc.split("/");
if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
}
-
- matches.add(match);
- if(!ContainerFlowConfig.unidirectional) {
- Match reverse = match.reverse();
- if (!match.equals(reverse)) {
- matches.add(reverse);
- }
- }
- return matches;
+ return match;
}
/*
public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
try {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
-
CompositeNode result = biDataService.readOperationalData(biPath);
- Class<? extends DataObject> targetType = path.getTargetType();
-
- if (Augmentation.class.isAssignableFrom(targetType)) {
- path = mappingService.fromDataDom(biPath);
- Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
- DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
- if (parentTo instanceof Augmentable<?>) {
- return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
- }
-
- }
- return mappingService.dataObjectFromDataDom(path, result);
-
+ return potentialAugmentationRead(path,biPath,result);
} catch (DeserializationException e) {
throw new IllegalStateException(e);
}
}
+ private DataObject potentialAugmentationRead(InstanceIdentifier<? extends DataObject> path, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, CompositeNode result) throws DeserializationException {
+ Class<? extends DataObject> targetType = path.getTargetType();
+ if (Augmentation.class.isAssignableFrom(targetType)) {
+ path = mappingService.fromDataDom(biPath);
+ Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
+ DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
+ if (parentTo instanceof Augmentable<?>) {
+ return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
+ }
+ }
+ return mappingService.dataObjectFromDataDom(path, result);
+ }
+
@Override
public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
try {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
CompositeNode result = biDataService.readConfigurationData(biPath);
- return mappingService.dataObjectFromDataDom(path, result);
+ return potentialAugmentationRead(path,biPath,result);
} catch (DeserializationException e) {
throw new IllegalStateException(e);
}
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActions;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActionsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionType;
assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
testNodeRemove();
}
+
+ @Test
+ public void putNodeWithAugmentation() throws Exception {
+
+ NodeBuilder nodeBuilder = new NodeBuilder();
+ nodeBuilder.setId(new NodeId(NODE_ID));
+ nodeBuilder.setKey(NODE_KEY);
+ FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
+ fnub.setHardware("Hardware Foo");
+ fnub.setManufacturer("Manufacturer Foo");
+ fnub.setSerialNumber("Serial Foo");
+ fnub.setDescription("Description Foo");
+ fnub.setSoftware("JUnit emulated");
+ FlowCapableNode fnu = fnub.build();
+
+ nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
+ DataModificationTransaction baseTransaction = baDataService.beginTransaction();
+ baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+ RpcResult<TransactionStatus> result = baseTransaction.commit().get();
+ assertEquals(TransactionStatus.COMMITED, result.getResult());
+
+ FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(InstanceIdentifier.builder(NODE_INSTANCE_ID_BA).augmentation(FlowCapableNode.class).toInstance());
+ assertNotNull(readedAugmentation);
+ assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
+
+ testPutNodeConnectorWithAugmentation();
+ testNodeRemove();
+ }
+ private void testPutNodeConnectorWithAugmentation() throws Exception {
+ NodeConnectorKey ncKey = new NodeConnectorKey(new NodeConnectorId("test:0:0"));
+ InstanceIdentifier<NodeConnector> ncPath = InstanceIdentifier.builder(NODE_INSTANCE_ID_BA)
+ .child(NodeConnector.class, ncKey).toInstance();
+ InstanceIdentifier<FlowCapableNodeConnector> ncAugmentPath = InstanceIdentifier.builder(ncPath)
+ .augmentation(FlowCapableNodeConnector.class).toInstance();
+
+ NodeConnectorBuilder nc = new NodeConnectorBuilder();
+ nc.setKey(ncKey);
+
+ FlowCapableNodeConnectorBuilder fncb = new FlowCapableNodeConnectorBuilder();
+ fncb.setName("Baz");
+ nc.addAugmentation(FlowCapableNodeConnector.class, fncb.build());
+
+ DataModificationTransaction baseTransaction = baDataService.beginTransaction();
+ baseTransaction.putOperationalData(ncPath, nc.build());
+ RpcResult<TransactionStatus> result = baseTransaction.commit().get();
+ assertEquals(TransactionStatus.COMMITED, result.getResult());
+
+ FlowCapableNodeConnector readedAugmentation = (FlowCapableNodeConnector) baDataService.readOperationalData(ncAugmentPath);
+ assertNotNull(readedAugmentation);
+ assertEquals(fncb.getName(), readedAugmentation.getName());
+ }
+
private void testNodeRemove() throws Exception {
DataModificationTransaction transaction = baDataService.beginTransaction();
transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public interface MountInstance extends NotificationService, DataBrokerService {
Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+
+ SchemaContext getSchemaContext();
}
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public interface MountProvisionInstance extends //
MountInstance,//
RpcProvisionRegistry,//
DataProviderService {
+ void setSchemaContext(SchemaContext optional);
+
}
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public class MountPointImpl implements MountProvisionInstance {
private final InstanceIdentifier mountPath;
+ private SchemaContext schemaContext;
+
public MountPointImpl(InstanceIdentifier path) {
this.mountPath = path;
rpcs = new RpcRouterImpl("");
// NOOP
}
+ public SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ public void setSchemaContext(SchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ }
+
class ReadWrapper implements DataReader<InstanceIdentifier, CompositeNode> {
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>0.2.3-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools.model</groupId>
<artifactId>ietf-inet-types</artifactId>
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
+import java.io.File;
+import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import javax.net.ssl.SSLContext;
import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
import org.opendaylight.protocol.framework.ReconnectStrategy;
import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
import org.osgi.framework.BundleContext;
import static com.google.common.base.Preconditions.*;
public final class NetconfConnectorModule extends org.opendaylight.controller.config.yang.md.sal.connector.netconf.AbstractNetconfConnectorModule
{
+ private static ExecutorService GLOBAL_PROCESSING_EXECUTOR = null;
+ private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
private BundleContext bundleContext;
public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
} else {
addressValue = getAddress().getIpv6Address().getValue();
}
-
*/
ReconnectStrategy strategy = new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null,
Long.valueOf(connectionAttempts), null);
-
- device.setStrategy(strategy);
+ device.setReconnectStrategy(strategy);
InetAddress addr = InetAddresses.forString(addressValue);
InetSocketAddress socketAddress = new InetSocketAddress(addr , getPort().intValue());
+
+
+ device.setProcessingExecutor(getGlobalProcessingExecutor());
+
device.setSocketAddress(socketAddress);
+ device.setEventExecutor(getEventExecutorDependency());
+ device.setDispatcher(createDispatcher());
+ device.setSchemaSourceProvider(getGlobalNetconfSchemaProvider(bundleContext));
+ getDomRegistryDependency().registerProvider(device, bundleContext);
+ device.start();
+ return device;
+ }
+
+ private ExecutorService getGlobalProcessingExecutor() {
+ if(GLOBAL_PROCESSING_EXECUTOR == null) {
+
+ GLOBAL_PROCESSING_EXECUTOR = Executors.newCachedThreadPool();
+
+ }
+ return GLOBAL_PROCESSING_EXECUTOR;
+ }
+
+ private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider(BundleContext bundleContext) {
+ if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
+ String storageFile = "cache/schema";
+ File directory = bundleContext.getDataFile(storageFile);
+ SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
+ GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory);
+ }
+ return GLOBAL_NETCONF_SOURCE_PROVIDER;
+ }
+
+ private NetconfClientDispatcher createDispatcher() {
EventLoopGroup bossGroup = getBossThreadGroupDependency();
EventLoopGroup workerGroup = getWorkerThreadGroupDependency();
- NetconfClientDispatcher dispatcher = null;
if(getTcpOnly()) {
- dispatcher = new NetconfClientDispatcher( bossGroup, workerGroup);
+ return new NetconfClientDispatcher( bossGroup, workerGroup);
} else {
AuthenticationHandler authHandler = new LoginPassword(getUsername(),getPassword());
- dispatcher = new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
+ return new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
}
- getDomRegistryDependency().registerProvider(device, bundleContext);
-
- device.start(dispatcher);
- return device;
}
public void setBundleContext(BundleContext bundleContext) {
import org.opendaylight.protocol.framework.ReconnectStrategy
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler
import org.opendaylight.controller.md.sal.common.api.data.DataModification
+import com.google.common.collect.FluentIterable
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl
+import java.io.InputStream
+import org.slf4j.LoggerFactory
+import org.slf4j.Logger
+import org.opendaylight.controller.netconf.client.AbstractNetconfClientNotifySessionListener
+import org.opendaylight.controller.netconf.client.NetconfClientSession
+import org.opendaylight.controller.netconf.api.NetconfMessage
+import io.netty.util.concurrent.EventExecutor
-class NetconfDevice implements
- Provider, //
- DataReader<InstanceIdentifier, CompositeNode>, //
- DataCommitHandler<InstanceIdentifier, CompositeNode>, //
- RpcImplementation, //
- AutoCloseable {
+import java.util.Map
+import java.util.Set
+import com.google.common.collect.ImmutableMap
+
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider
+import com.google.common.base.Optional
+import com.google.common.collect.ImmutableList
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders
+import static com.google.common.base.Preconditions.*;
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Future
+import org.opendaylight.controller.netconf.client.NetconfClientSessionListener
+import io.netty.util.concurrent.Promise
+import org.opendaylight.controller.netconf.util.xml.XmlElement
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.locks.ReentrantLock
+
+class NetconfDevice implements Provider, //
+DataReader<InstanceIdentifier, CompositeNode>, //
+DataCommitHandler<InstanceIdentifier, CompositeNode>, //
+RpcImplementation, //
+AutoCloseable {
var NetconfClient client;
@Property
var MountProvisionInstance mountInstance;
+ @Property
+ var EventExecutor eventExecutor;
+
+ @Property
+ var ExecutorService processingExecutor;
+
@Property
var InstanceIdentifier path;
@Property
- var ReconnectStrategy strategy;
+ var ReconnectStrategy reconnectStrategy;
+
+ @Property
+ var AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
+
+ private NetconfDeviceSchemaContextProvider schemaContextProvider
+
+ protected val Logger logger
Registration<DataReader<InstanceIdentifier, CompositeNode>> operReaderReg
Registration<DataReader<InstanceIdentifier, CompositeNode>> confReaderReg
Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> commitHandlerReg
-
+
val String name
MountProvisionService mountService
+
+ int messegeRetryCount = 5;
+
+ int messageTimeoutCount = 5 * 1000;
+
+ Set<QName> cachedCapabilities
+
+ @Property
+ var NetconfClientDispatcher dispatcher
-
+ static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
+
public new(String name) {
this.name = name;
+ this.logger = LoggerFactory.getLogger(NetconfDevice.name + "#" + name);
this.path = InstanceIdentifier.builder(INVENTORY_PATH).nodeWithKey(INVENTORY_NODE,
Collections.singletonMap(INVENTORY_ID, name)).toInstance;
}
- def start(NetconfClientDispatcher dispatcher) {
- client = NetconfClient.clientFor(name, socketAddress, strategy, dispatcher);
- confReaderReg = mountInstance.registerConfigurationReader(path, this);
- operReaderReg = mountInstance.registerOperationalReader(path, this);
- //commitHandlerReg = mountInstance.registerCommitHandler(path,this);
+ def start() {
+ checkState(dispatcher != null, "Dispatcher must be set.");
+ checkState(schemaSourceProvider != null, "Schema Source Provider must be set.")
+ checkState(eventExecutor != null, "Event executor must be set.");
+
+ val listener = new NetconfDeviceListener(this,eventExecutor);
+ val task = startClientTask(dispatcher, listener)
+ if(mountInstance != null) {
+ confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this);
+ operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this);
+ }
+ return processingExecutor.submit(task) as Future<Void>;
+
+ //commitHandlerReg = mountInstance.registerCommitHandler(path,this);
+ }
+
+ def Optional<SchemaContext> getSchemaContext() {
+ if (schemaContextProvider == null) {
+ return Optional.absent();
+ }
+ return schemaContextProvider.currentContext;
+ }
+
+ private def Runnable startClientTask(NetconfClientDispatcher dispatcher, NetconfDeviceListener listener) {
+ return [ |
+ logger.info("Starting Netconf Client on: {}", socketAddress);
+ client = NetconfClient.clientFor(name, socketAddress, reconnectStrategy, dispatcher, listener);
+ logger.debug("Initial capabilities {}", initialCapabilities);
+ var SchemaSourceProvider<String> delegate;
+ if (initialCapabilities.contains(NetconfMapping.IETF_NETCONF_MONITORING_MODULE)) {
+ delegate = new NetconfDeviceSchemaSourceProvider(this);
+ } else {
+ logger.info("Device does not support IETF Netconf Monitoring.", socketAddress);
+ delegate = SchemaSourceProviders.<String>noopProvider();
+ }
+ val sourceProvider = schemaSourceProvider.createInstanceFor(delegate);
+ schemaContextProvider = new NetconfDeviceSchemaContextProvider(this, sourceProvider);
+ schemaContextProvider.createContextFromCapabilities(initialCapabilities);
+ if (mountInstance != null && schemaContext.isPresent) {
+ mountInstance.schemaContext = schemaContext.get();
+ }
+ ]
}
override readConfigurationData(InstanceIdentifier path) {
- val result = invokeRpc(NETCONF_GET_CONFIG_QNAME, wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, path.toFilterStructure()));
+ val result = invokeRpc(NETCONF_GET_CONFIG_QNAME,
+ wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, path.toFilterStructure()));
val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME);
return data?.findNode(path) as CompositeNode;
}
override getSupportedRpcs() {
Collections.emptySet;
}
+
+ def createSubscription(String streamName) {
+ val it = ImmutableCompositeNode.builder()
+ QName = NETCONF_CREATE_SUBSCRIPTION_QNAME
+ addLeaf("stream",streamName);
+ invokeRpc(QName,toInstance())
+ }
override invokeRpc(QName rpc, CompositeNode input) {
val message = rpc.toRpcMessage(input);
- val result = client.sendMessage(message);
+ val result = client.sendMessage(message, messegeRetryCount, messageTimeoutCount);
return result.toRpcResult();
}
override onSessionInitiated(ProviderSession session) {
val dataBroker = session.getService(DataBrokerService);
-
-
-
+
val transaction = dataBroker.beginTransaction
- if(transaction.operationalNodeNotExisting) {
- transaction.putOperationalData(path,nodeWithId)
+ if (transaction.operationalNodeNotExisting) {
+ transaction.putOperationalData(path, nodeWithId)
}
- if(transaction.configurationNodeNotExisting) {
- transaction.putConfigurationData(path,nodeWithId)
+ if (transaction.configurationNodeNotExisting) {
+ transaction.putConfigurationData(path, nodeWithId)
}
transaction.commit().get();
mountService = session.getService(MountProvisionService);
- mountInstance = mountService.createOrGetMountPoint(path);
+ mountInstance = mountService?.createOrGetMountPoint(path);
}
-
+
def getNodeWithId() {
- val id = new SimpleNodeTOImpl(INVENTORY_ID,null,name);
- return new CompositeNodeTOImpl(INVENTORY_NODE,null,Collections.singletonList(id));
+ val id = new SimpleNodeTOImpl(INVENTORY_ID, null, name);
+ return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.singletonList(id));
}
-
+
def boolean configurationNodeNotExisting(DataModificationTransaction transaction) {
return null === transaction.readConfigurationData(path);
}
-
+
def boolean operationalNodeNotExisting(DataModificationTransaction transaction) {
return null === transaction.readOperationalData(path);
}
} else if (current instanceof CompositeNode) {
val currentComposite = (current as CompositeNode);
- current = currentComposite.getFirstCompositeByName(arg.nodeType);
+ current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision());
if (current == null) {
- current = currentComposite.getFirstSimpleByName(arg.nodeType);
+ current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision());
}
if (current == null) {
return null;
throw new UnsupportedOperationException("TODO: auto-generated method stub")
}
+ def getInitialCapabilities() {
+ val capabilities = client?.capabilities;
+ if (capabilities == null) {
+ return null;
+ }
+ if (cachedCapabilities == null) {
+ cachedCapabilities = FluentIterable.from(capabilities).filter[
+ contains("?") && contains("module=") && contains("revision=")].transform [
+ val parts = split("\\?");
+ val namespace = parts.get(0);
+ val queryParams = FluentIterable.from(parts.get(1).split("&"));
+ val revision = queryParams.findFirst[startsWith("revision=")].replaceAll("revision=", "");
+ val moduleName = queryParams.findFirst[startsWith("module=")].replaceAll("module=", "");
+ return QName.create(namespace, revision, moduleName);
+ ].toSet();
+ }
+ return cachedCapabilities;
+ }
+
override close() {
confReaderReg?.close()
operReaderReg?.close()
}
}
+
+package class NetconfDeviceListener extends NetconfClientSessionListener {
+
+ val NetconfDevice device
+ val EventExecutor eventExecutor
+
+ new(NetconfDevice device,EventExecutor eventExecutor) {
+ this.device = device
+ this.eventExecutor = eventExecutor
+ }
+
+ var Promise<NetconfMessage> messagePromise;
+ val promiseLock = new ReentrantLock;
+
+ override onMessage(NetconfClientSession session, NetconfMessage message) {
+ if (isNotification(message)) {
+ onNotification(session, message);
+ } else try {
+ promiseLock.lock
+ if (messagePromise != null) {
+ messagePromise.setSuccess(message);
+ messagePromise = null;
+ }
+ } finally {
+ promiseLock.unlock
+ }
+ }
+
+ /**
+ * Method intended to customize notification processing.
+ *
+ * @param session
+ * {@see
+ * NetconfClientSessionListener#onMessage(NetconfClientSession,
+ * NetconfMessage)}
+ * @param message
+ * {@see
+ * NetconfClientSessionListener#onMessage(NetconfClientSession,
+ * NetconfMessage)}
+ */
+ def void onNotification(NetconfClientSession session, NetconfMessage message) {
+ device.logger.debug("Received NETCONF notification.",message);
+ val domNotification = message?.toCompositeNode?.notificationBody;
+ if(domNotification != null) {
+ device?.mountInstance?.publish(domNotification);
+ }
+ }
+
+ private static def CompositeNode getNotificationBody(CompositeNode node) {
+ for(child : node.children) {
+ if(child instanceof CompositeNode) {
+ return child as CompositeNode;
+ }
+ }
+ }
+
+ override getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException {
+ val promise = promiseReply();
+ val messageAvailable = promise.await(attempts + attemptMsDelay);
+ if (messageAvailable) {
+ try {
+ return promise.get();
+ } catch (ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ throw new IllegalStateException("Unsuccessful after " + attempts + " attempts.");
+
+ // throw new TimeoutException("Message was not received on time.");
+ }
+
+ def Promise<NetconfMessage> promiseReply() {
+ promiseLock.lock
+ try {
+ if (messagePromise == null) {
+ messagePromise = eventExecutor.newPromise();
+ return messagePromise;
+ }
+ return messagePromise;
+ } finally {
+ promiseLock.unlock
+ }
+ }
+
+ def boolean isNotification(NetconfMessage message) {
+ val xmle = XmlElement.fromDomDocument(message.getDocument());
+ return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName());
+ }
+}
+
+package class NetconfDeviceSchemaContextProvider {
+
+ @Property
+ val NetconfDevice device;
+
+ @Property
+ val SchemaSourceProvider<InputStream> sourceProvider;
+
+ @Property
+ var Optional<SchemaContext> currentContext;
+
+ new(NetconfDevice device, SchemaSourceProvider<InputStream> sourceProvider) {
+ _device = device
+ _sourceProvider = sourceProvider
+ }
+
+ def createContextFromCapabilities(Iterable<QName> capabilities) {
+
+ val modelsToParse = ImmutableMap.<QName, InputStream>builder();
+ for (cap : capabilities) {
+ val source = sourceProvider.getSchemaSource(cap.localName, Optional.fromNullable(cap.formattedRevision));
+ if (source.present) {
+ modelsToParse.put(cap, source.get());
+ }
+ }
+ val context = tryToCreateContext(modelsToParse.build);
+ currentContext = Optional.fromNullable(context);
+ }
+
+ def SchemaContext tryToCreateContext(Map<QName, InputStream> modelsToParse) {
+ val parser = new YangParserImpl();
+ try {
+ val models = parser.parseYangModelsFromStreams(ImmutableList.copyOf(modelsToParse.values));
+ val result = parser.resolveSchemaContext(models);
+ return result;
+ } catch (Exception e) {
+ device.logger.debug("Error occured during parsing YANG schemas", e);
+ return null;
+ }
+ }
+}
+
+package class NetconfDeviceSchemaSourceProvider implements SchemaSourceProvider<String> {
+
+ val NetconfDevice device;
+
+ new(NetconfDevice device) {
+ this.device = device;
+ }
+
+ override getSchemaSource(String moduleName, Optional<String> revision) {
+ val it = ImmutableCompositeNode.builder() //
+ setQName(QName::create(NetconfState.QNAME, "get-schema")) //
+ addLeaf("format", "yang")
+ addLeaf("identifier", moduleName)
+ if (revision.present) {
+ addLeaf("version", revision.get())
+ }
+
+ device.logger.info("Loading YANG schema source for {}:{}", moduleName, revision)
+ val schemaReply = device.invokeRpc(getQName(), toInstance());
+
+ if (schemaReply.successful) {
+ val schemaBody = schemaReply.result.getFirstSimpleByName(
+ QName::create(NetconfState.QNAME.namespace, null, "data"))?.value;
+ device.logger.info("YANG Schema successfully received for: {}:{}", moduleName, revision);
+ return Optional.of(schemaBody as String);
+ }
+ return Optional.absent();
+ }
+}
import org.opendaylight.controller.sal.common.util.Rpcs
import java.util.List
import com.google.common.collect.ImmutableList
+import org.opendaylight.yangtools.yang.data.api.SimpleNode
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
class NetconfMapping {
public static val NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0")
- public static val NETCONF_QNAME = new QName(NETCONF_URI,null,"netconf");
- public static val NETCONF_RPC_QNAME = new QName(NETCONF_QNAME,"rpc");
- public static val NETCONF_GET_QNAME = new QName(NETCONF_QNAME,"get");
- public static val NETCONF_GET_CONFIG_QNAME = new QName(NETCONF_QNAME,"get-config");
- public static val NETCONF_SOURCE_QNAME = new QName(NETCONF_QNAME,"source");
- public static val NETCONF_RUNNING_QNAME = new QName(NETCONF_QNAME,"running");
- public static val NETCONF_RPC_REPLY_QNAME = new QName(NETCONF_QNAME,"rpc-reply");
- public static val NETCONF_OK_QNAME = new QName(NETCONF_QNAME,"ok");
- public static val NETCONF_DATA_QNAME = new QName(NETCONF_QNAME,"data");
+ public static val NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
+ public static val NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0")
- static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME,null,null));
- public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME,null,RUNNING);
-
- static val messageId = new AtomicInteger(0);
-
+ public static val NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
+ public static val NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+ public static val NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
+ public static val NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
+ public static val NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
+ public static val NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+ public static val NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
+ public static val NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
+ public static val NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
+ public static val NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
+ public static val NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
+ public static val NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"create-subscription");
+ public static val NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"cancel-subscription");
+ public static val IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04","ietf-netconf-monitoring");
+ static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(
+ new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME, null, null));
+ public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
+ static val messageId = new AtomicInteger(0);
static def Node<?> toFilterStructure(InstanceIdentifier identifier) {
var Node<?> previous = null;
- for (component : identifier.path.reverse) {
+ if(identifier.path.empty) {
+ return null;
+ }
+
+ for (component : identifier.path.reverseView) {
val Node<?> current = component.toNode(previous);
previous = current;
}
- return previous;
+ return filter("subtree",previous);
}
-
+
static def dispatch Node<?> toNode(NodeIdentifierWithPredicates argument, Node<?> node) {
val list = new ArrayList<Node<?>>();
- for( arg : argument.keyValues.entrySet) {
- list.add = new SimpleNodeTOImpl(arg.key,null,arg.value);
+ for (arg : argument.keyValues.entrySet) {
+ list.add = new SimpleNodeTOImpl(arg.key, null, arg.value);
}
- return new CompositeNodeTOImpl(argument.nodeType,null,list)
+ return new CompositeNodeTOImpl(argument.nodeType, null, list)
}
-
+
static def dispatch Node<?> toNode(PathArgument argument, Node<?> node) {
- if(node != null) {
- return new CompositeNodeTOImpl(argument.nodeType,null,Collections.singletonList(node));
+ if (node != null) {
+ return new CompositeNodeTOImpl(argument.nodeType, null, Collections.singletonList(node));
} else {
- return new SimpleNodeTOImpl(argument.nodeType,null,null);
+ return new SimpleNodeTOImpl(argument.nodeType, null, null);
}
}
}
static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node) {
- val rpcPayload = wrap(NETCONF_RPC_QNAME,node);
+ val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node));
val w3cPayload = NodeUtils.buildShadowDomTree(rpcPayload);
- w3cPayload.documentElement.setAttribute("message-id","m-"+ messageId.andIncrement);
+ w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement);
return new NetconfMessage(w3cPayload);
}
+
+ def static flattenInput(CompositeNode node) {
+ val inputQName = QName.create(node.nodeType,"input");
+ val input = node.getFirstCompositeByName(inputQName);
+ if(input == null) return node;
+ if(input instanceof CompositeNode) {
+
+ val nodes = ImmutableList.builder() //
+ .addAll(input.children) //
+ .addAll(node.children.filter[nodeType != inputQName]) //
+ .build()
+ return ImmutableCompositeNode.create(node.nodeType,nodes);
+ }
+
+ }
static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message) {
val rawRpc = message.document.toCompositeNode() as CompositeNode;
+
//rawRpc.
-
- return Rpcs.getRpcResult(true,rawRpc,Collections.emptySet());
+ return Rpcs.getRpcResult(true, rawRpc, Collections.emptySet());
}
-
-
- static def wrap(QName name,Node<?> node) {
- if(node != null) {
- return new CompositeNodeTOImpl(name,null,Collections.singletonList(node));
- }
- else {
- return new CompositeNodeTOImpl(name,null,Collections.emptyList());
+
+ static def wrap(QName name, Node<?> node) {
+ if (node != null) {
+ return new CompositeNodeTOImpl(name, null, Collections.singletonList(node));
+ } else {
+ return new CompositeNodeTOImpl(name, null, Collections.emptyList());
}
}
-
- static def wrap(QName name,Node<?> additional,Node<?> node) {
- if(node != null) {
- return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional,node));
+
+ static def wrap(QName name, Node<?> additional, Node<?> node) {
+ if (node != null) {
+ return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
+ } else {
+ return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional));
}
- else {
- return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional));
+ }
+
+ static def filter(String type, Node<?> node) {
+ val it = ImmutableCompositeNode.builder(); //
+ setQName(NETCONF_FILTER_QNAME);
+ setAttribute(NETCONF_TYPE_QNAME,type);
+ if (node != null) {
+ return add(node).toInstance();
+ } else {
+ return toInstance();
}
}
-
-
+
public static def Node<?> toCompositeNode(Document document) {
- return XmlDocumentUtils.toCompositeNode(document) as Node<?>
+ return XmlDocumentUtils.toNode(document) as Node<?>
}
}
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import com.google.common.base.Strings;
+
public class XmlDocumentUtils {
- public static CompositeNode toCompositeNode(Document doc) {
- return (CompositeNode) toCompositeNode(doc.getDocumentElement());
+ public static Node<?> toNode(Document doc) {
+ return toCompositeNode(doc.getDocumentElement());
}
private static Node<?> toCompositeNode(Element element) {
List<Node<?>> values = new ArrayList<>();
NodeList nodes = element.getChildNodes();
- boolean isSimpleObject = false;
+ boolean isSimpleObject = true;
String value = null;
for (int i = 0; i < nodes.getLength(); i++) {
org.w3c.dom.Node child = nodes.item(i);
isSimpleObject = false;
values.add(toCompositeNode((Element) child));
}
- if (!isSimpleObject && child instanceof org.w3c.dom.Text) {
+ if (isSimpleObject && child instanceof org.w3c.dom.Text) {
value = element.getTextContent();
- if (value.matches(".*\\w.*")) {
+ if (!Strings.isNullOrEmpty(value)) {
isSimpleObject = true;
- break;
}
}
}
--- /dev/null
+package org.opendaylight.controller.sal.connect.netconf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.io.StringReader;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ *
+ *
+ */
+public class YangModelInputStreamAdapter extends InputStream implements Delegator<InputStream> {
+
+ final String source;
+ final QName moduleIdentifier;
+ final InputStream delegate;
+
+
+
+ private YangModelInputStreamAdapter(String source, QName moduleIdentifier, InputStream delegate) {
+ super();
+ this.source = source;
+ this.moduleIdentifier = moduleIdentifier;
+ this.delegate = delegate;
+ }
+
+ public int read() throws IOException {
+ return delegate.read();
+ }
+
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ public int read(byte[] b) throws IOException {
+ return delegate.read(b);
+ }
+
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ return delegate.read(b, off, len);
+ }
+
+ public long skip(long n) throws IOException {
+ return delegate.skip(n);
+ }
+
+ public int available() throws IOException {
+ return delegate.available();
+ }
+
+ public void close() throws IOException {
+ delegate.close();
+ }
+
+ public void mark(int readlimit) {
+ delegate.mark(readlimit);
+ }
+
+ public void reset() throws IOException {
+ delegate.reset();
+ }
+
+ public boolean markSupported() {
+ return delegate.markSupported();
+ }
+
+ @Override
+ public InputStream getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public String toString() {
+ return "YangModelInputStreamAdapter [moduleIdentifier=" + moduleIdentifier + ", delegate=" + delegate + "]";
+ }
+
+ public static YangModelInputStreamAdapter create(QName name, String module) {
+ InputStream stringInput = new StringBufferInputStream(module);
+ return new YangModelInputStreamAdapter(null, name, stringInput );
+ }
+}
}
}
}
+
+ container event-executor {
+ uses config:service-ref {
+ refine type {
+ config:required-identity netty:netty-event-executor;
+ }
+ }
+ }
}
}
}
\ No newline at end of file
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
SchemaService schemaService = session.getService(SchemaService.class);
listenerRegistration = schemaService.registerSchemaServiceListener(ControllerContext.getInstance());
ControllerContext.getInstance().setSchemas(schemaService.getGlobalContext());
+ ControllerContext.getInstance().setMountService(session.getService(MountService.class));
}
@Override
package org.opendaylight.controller.sal.rest.impl;
-import java.util.Set;
-
import javax.activation.UnsupportedDataTypeException;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
-import org.opendaylight.controller.sal.restconf.impl.RestCodec;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.YangNode;
-import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import com.google.common.base.Preconditions;
public class XmlMapper {
-
- private final Logger logger = LoggerFactory.getLogger(XmlMapper.class);
-
public Document write(CompositeNode data, DataNodeContainer schema) throws UnsupportedDataTypeException {
- Preconditions.checkNotNull(data);
- Preconditions.checkNotNull(schema);
-
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- Document doc = null;
- try {
- DocumentBuilder bob = dbf.newDocumentBuilder();
- doc = bob.newDocument();
- } catch (ParserConfigurationException e) {
- return null;
- }
-
- if (schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode) {
- doc.appendChild(translateToXmlAndReturnRootElement(doc, data, schema));
- return doc;
- } else {
- throw new UnsupportedDataTypeException(
- "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
- }
- }
-
- private Element translateToXmlAndReturnRootElement(Document doc, Node<?> data, YangNode schema)
- throws UnsupportedDataTypeException {
- QName dataType = data.getNodeType();
- Element itemEl = doc.createElementNS(dataType.getNamespace().toString(), dataType.getLocalName());
- if (data instanceof SimpleNode<?>) {
- if (schema instanceof LeafListSchemaNode) {
- writeValueOfNodeByType(itemEl, (SimpleNode<?>) data, ((LeafListSchemaNode) schema).getType(), (DataSchemaNode) schema);
- } else if (schema instanceof LeafSchemaNode) {
- writeValueOfNodeByType(itemEl, (SimpleNode<?>) data, ((LeafSchemaNode) schema).getType(), (DataSchemaNode) schema);
- } else {
- Object value = data.getValue();
- if (value != null) {
- itemEl.setTextContent(String.valueOf(value));
- }
- }
- } else { // CompositeNode
- for (Node<?> child : ((CompositeNode) data).getChildren()) {
- DataSchemaNode childSchema = null;
- if(schema != null){
- childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes());
- if (logger.isDebugEnabled()) {
- if (childSchema == null) {
- logger.debug("Probably the data node \"" + ((child == null) ? "" : child.getNodeType().getLocalName())
- + "\" is not conform to schema");
- }
- }
- }
- itemEl.appendChild(translateToXmlAndReturnRootElement(doc, child, childSchema));
- }
- }
- return itemEl;
+ return XmlDocumentUtils.toDocument(data, schema, XmlDocumentUtils.defaultValueCodecProvider());
}
-
- private void writeValueOfNodeByType(Element element, SimpleNode<?> node, TypeDefinition<?> type, DataSchemaNode schema) {
-
- TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(type);
-
- if (baseType instanceof IdentityrefTypeDefinition) {
- if (node.getValue() instanceof QName) {
- IdentityValuesDTO valueDTO = (IdentityValuesDTO) RestCodec.from(type).serialize(node.getValue());
- IdentityValue value = valueDTO.getValuesWithNamespaces().get(0);
- String prefix = "x";
- if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
- prefix = value.getPrefix();
- }
- element.setAttribute("xmlns:" + prefix, value.getNamespace());
- element.setTextContent(prefix + ":" + value.getValue());
- } else {
- logger.debug("Value of " + baseType.getQName().getNamespace() + ":"
- + baseType.getQName().getLocalName() + " is not instance of " + QName.class + " but is " + node.getValue().getClass());
- element.setTextContent(String.valueOf(node.getValue()));
- }
- } else {
- if (node.getValue() != null) {
- String value = String.valueOf(RestCodec.from(baseType).serialize(node.getValue()));
- if (value.equals("null")) {
- value = String.valueOf(node.getValue());
- }
- element.setTextContent(value);
- }
- }
- }
-
- private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
- if (dataSchemaNode != null && node != null) {
- for (DataSchemaNode dsn : dataSchemaNode) {
- if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
- return dsn;
- } else if (dsn instanceof ChoiceNode) {
- for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
- DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
- if (foundDsn != null) {
- return foundDsn;
- }
- }
- }
- }
- }
- return null;
- }
-
}
import org.slf4j.LoggerFactory
import static com.google.common.base.Preconditions.*
+import org.opendaylight.controller.sal.core.api.mount.MountService
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
class ControllerContext implements SchemaServiceListener {
val static LOG = LoggerFactory.getLogger(ControllerContext)
val static ControllerContext INSTANCE = new ControllerContext
val static NULL_VALUE = "null"
- var SchemaContext schemas;
+ @Property
+ var SchemaContext globalSchema;
+
+ @Property
+ var MountService mountService;
private val BiMap<URI, String> uriToModuleName = HashBiMap.create();
private val Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
}
private def void checkPreconditions() {
- if (schemas === null) {
+ if (globalSchema === null) {
throw new ResponseException(Response.Status.SERVICE_UNAVAILABLE, RestconfProvider::NOT_INITALIZED_MSG)
}
}
if (pathArgs.head.empty) {
pathArgs.remove(0)
}
- val schemaNode = ret.collectPathArguments(pathArgs, restconfInstance.findModule);
+ val schemaNode = ret.collectPathArguments(pathArgs, globalSchema.findModule(pathArgs.head));
if (schemaNode === null) {
return null
}
return new InstanceIdWithSchemaNode(ret.toInstance, schemaNode)
}
- private def findModule(String restconfInstance) {
- checkPreconditions
- checkNotNull(restconfInstance);
- val pathArgs = restconfInstance.split("/");
- if (pathArgs.empty) {
- return null;
- }
- val modulWithFirstYangStatement = pathArgs.filter[s|s.contains(":")].head
- val startModule = modulWithFirstYangStatement.toModuleName();
- return getLatestModule(startModule)
+ private static def findModule(SchemaContext context,String argument) {
+ //checkPreconditions
+ checkNotNull(argument);
+ val startModule = argument.toModuleName();
+ return context.getLatestModule(startModule)
}
- def getLatestModule(String moduleName) {
- checkPreconditions
+ static def getLatestModule(SchemaContext schema,String moduleName) {
+ checkArgument(schema != null);
checkArgument(moduleName !== null && !moduleName.empty)
- val modules = schemas.modules.filter[m|m.name == moduleName]
+ val modules = schema.modules.filter[m|m.name == moduleName]
var latestModule = modules.head
for (module : modules) {
if (module.revision.after(latestModule.revision)) {
val elements = path.path;
val ret = new StringBuilder();
val startQName = elements.get(0).nodeType;
- val initialModule = schemas.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
+ val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
var node = initialModule as DataSchemaNode;
for (element : elements) {
node = node.childByQName(element.nodeType);
checkPreconditions
var module = uriToModuleName.get(namespace)
if (module === null) {
- val moduleSchemas = schemas.findModuleByNamespace(namespace);
+ val moduleSchemas = globalSchema.findModuleByNamespace(namespace);
if(moduleSchemas === null) return null
var latestModule = moduleSchemas.head
for (m : moduleSchemas) {
def findNamespaceByModule(String module) {
var namespace = moduleNameToUri.get(module)
if (namespace === null) {
- val moduleSchemas = schemas.modules.filter[it|it.name.equals(module)]
+ val moduleSchemas = globalSchema.modules.filter[it|it.name.equals(module)]
var latestModule = moduleSchemas.head
for (m : moduleSchemas) {
if (m.revision.after(latestModule.revision)) {
checkPreconditions
var module = uriToModuleName.get(qname.namespace)
if (module === null) {
- val moduleSchema = schemas.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
+ val moduleSchema = globalSchema.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
if(moduleSchema === null) throw new IllegalArgumentException()
uriToModuleName.put(qname.namespace, moduleSchema.name)
module = moduleSchema.name;
}
val nodeRef = strings.head;
- val nodeName = nodeRef.toNodeName();
- val targetNode = parentNode.getDataChildByName(nodeName);
- if (targetNode === null) {
- val children = parentNode.childNodes
- for (child : children) {
- if (child instanceof ChoiceNode) {
- val choice = child as ChoiceNode
- for (caze : choice.cases) {
- val result = builder.collectPathArguments(strings, caze as DataNodeContainer);
- if (result !== null)
- return result
- }
- }
- }
+ val nodeName = nodeRef.toNodeName;
+ var targetNode = parentNode.findInstanceDataChild(nodeName);
+ if (targetNode instanceof ChoiceNode) {
return null
}
- if (targetNode instanceof ChoiceNode) {
+
+ if (targetNode === null) {
+ // Node is possibly in other mount point
+ val partialPath = builder.toInstance;
+ val mountPointSchema = mountService?.getMountPoint(partialPath)?.schemaContext;
+ if(mountPointSchema != null) {
+ return builder.collectPathArguments(strings, mountPointSchema.findModule(strings.head));
+ }
return null
}
+
// Number of consumed elements
var consumed = 1;
return targetNode
}
+
+ static def DataSchemaNode findInstanceDataChild(DataNodeContainer container, String name) {
+ // FIXME: Add namespace comparison
+ var potentialNode = container.getDataChildByName(name);
+ if(potentialNode.instantiatedDataSchema) {
+ return potentialNode;
+ }
+ val allCases = container.childNodes.filter(ChoiceNode).map[cases].flatten
+ for (caze : allCases) {
+ potentialNode = caze.findInstanceDataChild(name);
+ if(potentialNode != null) {
+ return potentialNode;
+ }
+ }
+ return null;
+ }
+
+ static def boolean isInstantiatedDataSchema(DataSchemaNode node) {
+ switch node {
+ LeafSchemaNode: return true
+ LeafListSchemaNode: return true
+ ContainerSchemaNode: return true
+ ListSchemaNode: return true
+ default: return false
+ }
+ }
private def void addKeyValue(HashMap<QName, Object> map, DataSchemaNode node, String uriValue) {
checkNotNull(uriValue);
map.put(node.QName, decoded);
}
- private def String toModuleName(String str) {
+ private static def String toModuleName(String str) {
checkNotNull(str)
if (str.contains(":")) {
val args = str.split(":");
private def QName toQName(String name) {
val module = name.toModuleName;
val node = name.toNodeName;
- val namespace = FluentIterable.from(schemas.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
+ val namespace = FluentIterable.from(globalSchema.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
.transform[QName.create(namespace,revision,it.name)].findFirst[module == localName]
;
return QName.create(namespace,node);
}
override onGlobalContextUpdated(SchemaContext context) {
- this.schemas = context;
+ this.globalSchema = context;
for (operation : context.operations) {
val qname = operation.QName;
qnameToRpc.put(qname, operation);
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.Mock;
+
+import static org.mockito.Mockito.*;
+
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@BeforeClass
public static void init() throws FileNotFoundException {
- Set<Module> allModules = TestUtils.loadModules(ControllerContextTest.class.getResource("/full-versions/yangs").getPath());
+ Set<Module> allModules = TestUtils.loadModules(ControllerContextTest.class.getResource("/full-versions/yangs")
+ .getPath());
SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
controllerContext.setSchemas(schemaContext);
}
@Test
public void testToInstanceIdentifierList() throws FileNotFoundException {
- InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:userWithoutClass/foo");
+ InstanceIdWithSchemaNode instanceIdentifier = controllerContext
+ .toInstanceIdentifier("simple-nodes:userWithoutClass/foo");
assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass");
instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:userWithoutClass/foo/full-name");
}
+ @Test
+ public void testToInstanceIdentifierMountPoint() throws FileNotFoundException {
+ try {
+ String mountPointPath = "simple-nodes:user/foo/boo";
+ String nestedPath = "simple-nodes:user/foo/boo/simple-nodes:users";
+ InstanceIdWithSchemaNode mountInstanceIdentifier = controllerContext.toInstanceIdentifier(mountPointPath);
+ assertEquals("user", mountInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+ MountInstance mountInstance = mock(MountInstance.class);
+ MountService mountService = mock(MountService.class);
+
+ controllerContext.setMountService(mountService);
+ // when(mountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(null);
+
+ when(mountService.getMountPoint(eq(mountInstanceIdentifier.getInstanceIdentifier()))).thenReturn(
+ mountInstance);
+
+ when(mountInstance.getSchemaContext()).thenReturn(controllerContext.getGlobalSchema());
+
+ InstanceIdWithSchemaNode mountedInstanceIdentifier = controllerContext.toInstanceIdentifier(nestedPath);
+ assertEquals("users", mountedInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+ mountedInstanceIdentifier = controllerContext.toInstanceIdentifier(mountPointPath + "/" + mountPointPath);
+ assertEquals("user", mountedInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+ mountedInstanceIdentifier = controllerContext
+ .toInstanceIdentifier("simple-nodes:user/foo/var/simple-nodes:users");
+ assertNull(mountedInstanceIdentifier);
+
+ } finally {
+ controllerContext.setMountService(null);
+ }
+
+ }
+
@Test
public void testToInstanceIdentifierContainer() throws FileNotFoundException {
InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:users");
assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "users");
assertTrue(instanceIdentifier.getSchemaNode() instanceof ContainerSchemaNode);
- assertEquals(2, ((ContainerSchemaNode)instanceIdentifier.getSchemaNode()).getChildNodes().size());
+ assertEquals(2, ((ContainerSchemaNode) instanceIdentifier.getSchemaNode()).getChildNodes().size());
}
@Test
public void testToInstanceIdentifierChoice() throws FileNotFoundException {
InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/beer");
assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "beer");
-
+
instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/snack");
assertNull(instanceIdentifier);
-
+
instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/sports-arena");
assertNull(instanceIdentifier);
-
+
instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/snack/sports-arena");
assertNull(instanceIdentifier);
-
+
}
}
public void testReadOperationalData() throws UnsupportedEncodingException, FileNotFoundException {
String uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+
CompositeNode loadedCompositeNode = TestUtils.loadCompositeNodeWithXmlTreeBuilder("/parts/ietf-interfaces_interfaces.xml");
when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
return new NetconfClient(clientLabelForLogging,address,strat,netconfClientDispatcher);
}
+ public static NetconfClient clientFor(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat, NetconfClientDispatcher netconfClientDispatcher,NetconfClientSessionListener listener) throws InterruptedException {
+ return new NetconfClient(clientLabelForLogging,address,strat,netconfClientDispatcher,listener);
+ }
+
public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs,
NetconfClientDispatcher netconfClientDispatcher) throws InterruptedException {
this(clientLabelForLogging, address,
DEFAULT_CONNECT_TIMEOUT), netconfClientDispatcher);
}
+ public NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat,
+ NetconfClientDispatcher netconfClientDispatcher, NetconfClientSessionListener listener) throws InterruptedException{
+ this.label = clientLabelForLogging;
+ dispatch = netconfClientDispatcher;
+ sessionListener = listener;
+ Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
+ this.address = address;
+ clientSession = get(clientFuture);
+ this.sessionId = clientSession.getSessionId();
+ }
+
public NetconfMessage sendMessage(NetconfMessage message) {
return sendMessage(message, 5, 1000);
}
public NetconfMessage sendMessage(NetconfMessage message, int attempts, int attemptMsDelay) {
long startTime = System.currentTimeMillis();
Preconditions.checkState(clientSession.isUp(), "Session was not up yet");
+ //logger.debug("Sending message: {}",XmlUtil.toString(message.getDocument()));
clientSession.sendMessage(message);
try {
return sessionListener.getLastMessage(attempts, attemptMsDelay);