<artifactId>config-netconf-connector</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-impl</artifactId>
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupListener;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
import org.slf4j.LoggerFactory;
@SuppressWarnings("unused")
-public class GroupConsumerImpl implements IForwardingRulesManager {
+public class GroupConsumerImpl {
protected static final Logger logger = LoggerFactory.getLogger(GroupConsumerImpl.class);
private final GroupEventListener groupEventListener = new GroupEventListener();
private SalGroupService groupService;
private GroupDataCommitHandler groupCommitHandler;
- private ConcurrentMap<GroupKey, Group> originalSwGroupView;
- private ConcurrentMap<GroupKey, Group> installedSwGroupView;
-
- private ConcurrentMap<Node, List<Group>> nodeGroups;
- private ConcurrentMap<GroupKey, Group> inactiveGroups;
-
- private IClusterContainerServices clusterGroupContainerService = null;
private IContainer container;
public GroupConsumerImpl() {
InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Groups.class).toInstance();
- groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
-
- if (!(cacheStartup())) {
- logger.error("Unanle to allocate/retrieve group cache");
- System.out.println("Unable to allocate/retrieve group cache");
- }
+ groupService = FRMConsumerImpl.getProviderSession().getRpcService(SalGroupService.class);
if (null == groupService) {
logger.error("Consumer SAL Group Service is down or NULL. FRM may not function as intended");
groupCommitHandler = new GroupDataCommitHandler();
FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, groupCommitHandler);
- clusterGroupContainerService = FRMConsumerImpl.getClusterContainerService();
- container = FRMConsumerImpl.getContainer();
- }
-
- private boolean allocateGroupCaches() {
- if (this.clusterGroupContainerService == null) {
- logger.warn("Group: Un-initialized clusterGroupContainerService, can't create cache");
- return false;
- }
-
- try {
- clusterGroupContainerService.createCache("frm.originalSwGroupView",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.installedSwGroupView",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.inactiveGroups",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- clusterGroupContainerService.createCache("frm.nodeGroups",
- EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
-
- // TODO for cluster mode
- /*
- * clusterGroupContainerService.createCache(WORK_STATUS_CACHE,
- * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
- * IClusterServices.cacheMode.ASYNC));
- *
- * clusterGroupContainerService.createCache(WORK_ORDER_CACHE,
- * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
- * IClusterServices.cacheMode.ASYNC));
- */
-
- } catch (CacheConfigException cce) {
- logger.error("Group CacheConfigException");
- return false;
-
- } catch (CacheExistException cce) {
- logger.error(" Group CacheExistException");
- }
-
- return true;
- }
-
- private void nonClusterGroupObjectCreate() {
- originalSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
- installedSwGroupView = new ConcurrentHashMap<GroupKey, Group>();
- nodeGroups = new ConcurrentHashMap<Node, List<Group>>();
- inactiveGroups = new ConcurrentHashMap<GroupKey, Group>();
- }
-
- @SuppressWarnings({ "unchecked" })
- private boolean retrieveGroupCaches() {
- ConcurrentMap<?, ?> map;
-
- if (this.clusterGroupContainerService == null) {
- logger.warn("Group: un-initialized clusterGroupContainerService, can't retrieve cache");
- nonClusterGroupObjectCreate();
- return false;
- }
+ }
- map = clusterGroupContainerService.getCache("frm.originalSwGroupView");
- if (map != null) {
- originalSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(originalSwGroupView) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.installedSwGroupView");
- if (map != null) {
- installedSwGroupView = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(installedSwGroupView) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.inactiveGroups");
- if (map != null) {
- inactiveGroups = (ConcurrentMap<GroupKey, Group>) map;
- } else {
- logger.error("Retrieval of cache(inactiveGroups) failed");
- return false;
- }
-
- map = clusterGroupContainerService.getCache("frm.nodeGroups");
- if (map != null) {
- nodeGroups = (ConcurrentMap<Node, List<Group>>) map;
- } else {
- logger.error("Retrieval of cache(nodeGroup) failed");
- return false;
- }
-
- return true;
- }
-
- private boolean cacheStartup() {
- if (allocateGroupCaches()) {
- if (retrieveGroupCaches()) {
- return true;
- }
- }
-
- return false;
- }
-
- public Status validateGroup(Group group, FRMUtil.operation operation) {
- String containerName;
+ public Status validateGroup(Group group) {
String groupName;
Iterator<Bucket> bucketIterator;
boolean returnResult;
Buckets groupBuckets;
- if (null != group) {
- containerName = group.getContainerName();
-
- if (null == containerName) {
- containerName = GlobalConstants.DEFAULT.toString();
- } else if (!FRMUtil.isNameValid(containerName)) {
- logger.error("Container Name is invalid %s" + containerName);
- return new Status(StatusCode.BADREQUEST, "Container Name is invalid");
- }
-
+ if (null != group) {
groupName = group.getGroupName();
if (!FRMUtil.isNameValid(groupName)) {
logger.error("Group Name is invalid %s" + groupName);
* @param path
* @param dataObject
*/
- private Status updateGroup(InstanceIdentifier<?> path, Group groupUpdateDataObject) {
- GroupKey groupKey = groupUpdateDataObject.getKey();
+ private void updateGroup(InstanceIdentifier<?> path,
+ Group originalGroupDataObject, Group updatedGroupDataObject) {
+
+ GroupKey groupKey = updatedGroupDataObject.getKey();
+ // Node nodeInstanceID = path.firstIdentifierOf("Node");
UpdatedGroupBuilder updateGroupBuilder = null;
-
- Status groupOperationStatus = validateGroup(groupUpdateDataObject, FRMUtil.operation.UPDATE);
+ Status groupOperationStatus = validateGroup(updatedGroupDataObject);
if (!groupOperationStatus.isSuccess()) {
- logger.error("Group data object validation failed %s" + groupUpdateDataObject.getGroupName());
- return groupOperationStatus;
+ logger.error("Group data object validation failed %s" + updatedGroupDataObject.getGroupName());
+ return;
}
- UpdateGroupInputBuilder groupData = new UpdateGroupInputBuilder();
- updateGroupBuilder = new UpdatedGroupBuilder();
- updateGroupBuilder.setGroupId(new GroupId(groupUpdateDataObject.getId()));
- updateGroupBuilder.fieldsFrom(groupUpdateDataObject);
- groupData.setUpdatedGroup(updateGroupBuilder.build());
- groupService.updateGroup(groupData.build());
- return groupOperationStatus;
+ UpdateGroupInputBuilder groupInputBuilder = new UpdateGroupInputBuilder();
+ groupInputBuilder.setNode(updatedGroupDataObject.getNode());
+ updateGroupBuilder = new UpdatedGroupBuilder(updatedGroupDataObject);
+ updateGroupBuilder.setGroupId(new GroupId(updatedGroupDataObject.getId()));
+ groupInputBuilder.setUpdatedGroup(updateGroupBuilder.build());
+ OriginalGroupBuilder originalGroupBuilder = new OriginalGroupBuilder(originalGroupDataObject);
+ groupInputBuilder.setOriginalGroup(originalGroupBuilder.build());
+ groupService.updateGroup(groupInputBuilder.build());
+ return;
}
/**
* @param path
* @param dataObject
*/
- private Status addGroup(InstanceIdentifier<?> path, Group groupAddDataObject) {
+ private void addGroup(InstanceIdentifier<?> path, Group groupAddDataObject) {
GroupKey groupKey = groupAddDataObject.getKey();
- Status groupOperationStatus = validateGroup(groupAddDataObject, FRMUtil.operation.ADD);
+ Status groupOperationStatus = validateGroup(groupAddDataObject);
if (!groupOperationStatus.isSuccess()) {
logger.error("Group data object validation failed %s" + groupAddDataObject.getGroupName());
- return groupOperationStatus;
+ return;
}
AddGroupInputBuilder groupData = new AddGroupInputBuilder();
groupData.setGroupType(groupAddDataObject.getGroupType());
groupData.setNode(groupAddDataObject.getNode());
groupService.addGroup(groupData.build());
- return groupOperationStatus;
+ return;
}
/**
* @param path
* @param dataObject
*/
- private Status removeGroup(InstanceIdentifier<?> path, Group groupRemoveDataObject) {
+ private void removeGroup(InstanceIdentifier<?> path, Group groupRemoveDataObject) {
GroupKey groupKey = groupRemoveDataObject.getKey();
- Status groupOperationStatus = validateGroup(groupRemoveDataObject, FRMUtil.operation.ADD);
+ Status groupOperationStatus = validateGroup(groupRemoveDataObject);
if (!groupOperationStatus.isSuccess()) {
logger.error("Group data object validation failed %s" + groupRemoveDataObject.getGroupName());
- return groupOperationStatus;
+ return;
}
RemoveGroupInputBuilder groupData = new RemoveGroupInputBuilder();
groupData.setGroupType(groupRemoveDataObject.getGroupType());
groupData.setNode(groupRemoveDataObject.getNode());
groupService.removeGroup(groupData.build());
- return groupOperationStatus;
+ return;
}
private RpcResult<Void> commitToPlugin(InternalTransaction transaction) {
- for (Entry<InstanceIdentifier<?>, Group> entry : transaction.additions.entrySet()) {
-
- if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) {
- transaction.additions.remove(entry.getKey());
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
-
- for (Entry<InstanceIdentifier<?>, Group> entry : transaction.updates.entrySet()) {
-
- if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) {
- transaction.updates.remove(entry.getKey());
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
+ DataModification<InstanceIdentifier<?>, DataObject> modification = transaction.modification;
+ //get created entries
+ Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> createdEntries =
+ modification.getCreatedConfigurationData().entrySet();
+
+ //get updated entries
+ Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> updatedEntries =
+ new HashSet<Entry<InstanceIdentifier<? extends DataObject>, DataObject>>();
+
+ updatedEntries.addAll(modification.getUpdatedConfigurationData().entrySet());
+ updatedEntries.removeAll(createdEntries);
- for (InstanceIdentifier<?> groupId : transaction.removals) {
- DataObject removeValue = transaction.getModification().getOriginalConfigurationData().get(groupId);
-
- if(removeValue instanceof Group) {
- if(!removeGroup(groupId, (Group)removeValue).isSuccess()) {
- return Rpcs.getRpcResult(false, null, Collections.<RpcError>emptySet());
- }
- }
+ //get removed entries
+ Set<InstanceIdentifier<? extends DataObject>> removeEntriesInstanceIdentifiers =
+ modification.getRemovedConfigurationData();
+
+ for (Entry<InstanceIdentifier<? extends DataObject >, DataObject> entry : createdEntries) {
+ if(entry.getValue() instanceof Group) {
+ addGroup(entry.getKey(), (Group)entry.getValue());
+ }
+ }
+
+ for (Entry<InstanceIdentifier<?>, DataObject> entry : updatedEntries) {
+ if(entry.getValue() instanceof Group) {
+ Group originalGroup = (Group) modification.getOriginalConfigurationData().get(entry.getKey());
+ Group updatedGroup = (Group) entry.getValue();
+ updateGroup(entry.getKey(), originalGroup, updatedGroup);
+ }
+ }
+
+ for (InstanceIdentifier<?> instanceId : removeEntriesInstanceIdentifiers ) {
+ DataObject removeValue = modification.getOriginalConfigurationData().get(instanceId);
+ if(removeValue instanceof Group) {
+ removeGroup(instanceId, (Group)removeValue);
+ }
}
return Rpcs.getRpcResult(true, null, Collections.<RpcError>emptySet());
@Override
public DataCommitTransaction<InstanceIdentifier<?>, DataObject> requestCommit(
- DataModification<InstanceIdentifier<?>, DataObject> modification) {
- // We should verify transaction
- System.out.println("Coming in GroupDatacommitHandler");
+ DataModification<InstanceIdentifier<?>, DataObject> modification) {
InternalTransaction transaction = new InternalTransaction(modification);
transaction.prepareUpdate();
return transaction;
}
}
- private final class InternalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
+ private final class InternalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
private final DataModification<InstanceIdentifier<?>, DataObject> modification;
-
- @Override
- public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
- return modification;
- }
-
- public InternalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
+
+ public InternalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
this.modification = modification;
}
-
- Map<InstanceIdentifier<?>, Group> additions = new HashMap<>();
- Map<InstanceIdentifier<?>, Group> updates = new HashMap<>();
- Set<InstanceIdentifier<?>> removals = new HashSet<>();
-
+
/**
* We create a plan which flows will be added, which will be updated and
* which will be removed based on our internal state.
*/
void prepareUpdate() {
- Set<Entry<InstanceIdentifier<?>, DataObject>> groupAdded = modification.getCreatedConfigurationData().entrySet();
- for (Entry<InstanceIdentifier<?>, DataObject> entry : groupAdded) {
- if (entry.getValue() instanceof Group) {
- Group group = (Group) entry.getValue();
- additions.put(entry.getKey(), group);
- }
-
- }
-
- Set<Entry<InstanceIdentifier<?>, DataObject>> groupUpdate = modification.getUpdatedConfigurationData().entrySet();
- for (Entry<InstanceIdentifier<?>, DataObject> entry : groupUpdate) {
- if (entry.getValue() instanceof Group) {
- Group group = (Group) entry.getValue();
- ///will be fixed once getUpdatedConfigurationData returns only updated data not created data with it.
- if (additions.containsKey(entry.getKey())) {
- updates.put(entry.getKey(), group);
- }
- }
-
- }
-
- removals = modification.getRemovedConfigurationData();
}
/**
@Override
public RpcResult<Void> finish() throws IllegalStateException {
- RpcResult<Void> rpcStatus = commitToPlugin(this);
- // We return true if internal transaction is successful.
- // return Rpcs.getRpcResult(true, null, Collections.emptySet());
+ RpcResult<Void> rpcStatus = commitToPlugin(this);
return rpcStatus;
}
*
*/
@Override
- public RpcResult<Void> rollback() throws IllegalStateException {
- // NOOP - we did not modified any internal state during
- // requestCommit phase
- // return Rpcs.getRpcResult(true, null, Collections.emptySet());
+ public RpcResult<Void> rollback() throws IllegalStateException {
+
+ ///needs to be implemented as per gerrit 3314
return Rpcs.getRpcResult(true, null, Collections.<RpcError>emptySet());
+ }
+ @Override
+ public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
+ return modification;
}
}
}
}
-
- @Override
- public List<DataObject> get() {
-
- List<DataObject> orderedList = new ArrayList<DataObject>();
- Collection<Group> groupList = originalSwGroupView.values();
- for (Iterator<Group> iterator = groupList.iterator(); iterator.hasNext();) {
- orderedList.add(iterator.next());
- }
- return orderedList;
- }
-
- @Override
- public DataObject getWithName(String name, Node n) {
-
- if (this instanceof GroupConsumerImpl) {
- Collection<Group> groupList = originalSwGroupView.values();
- for (Iterator<Group> iterator = groupList.iterator(); iterator.hasNext();) {
- Group group = iterator.next();
- if (group.getNode().equals(n) && group.getGroupName().equals(name)) {
-
- return group;
- }
- }
- }
- return null;
- }
-}
+ }
<artifactId>netconf-impl</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>netconf-client</artifactId>
mavenBundle(CONTROLLER, "logback-config").versionAsInProject(), //
mavenBundle(CONTROLLER, "config-persister-api").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-api").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "ietf-netconf-monitoring").versionAsInProject(), //
+ mavenBundle(CONTROLLER, "netconf-monitoring").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-client").versionAsInProject(), //
mavenBundle(CONTROLLER, "netconf-util").versionAsInProject(), //
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);
} else { // CompositeNode
for (Node<?> child : ((CompositeNode) data).getChildren()) {
DataSchemaNode childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes());
- if (childSchema == null) {
- throw new UnsupportedDataTypeException("Probably the data node \""
- + child.getNodeType().getLocalName() + "\" is not conform to schema");
+ 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));
}
}
private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
- 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;
+ 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;
+ }
}
}
}
public class Get extends AbstractConfigNetconfOperation {
- public static final String GET = "get";
-
private final YangStoreSnapshot yangStoreSnapshot;
private static final Logger logger = LoggerFactory.getLogger(Get.class);
}
private static void checkXml(XmlElement xml) {
- xml.checkName(GET);
+ xml.checkName(XmlNetconfConstants.GET);
xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
// Filter option - unsupported
if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0)
- throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + GET);
+ throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + XmlNetconfConstants.GET);
}
@Override
protected String getOperationName() {
- return GET;
+ return XmlNetconfConstants.GET;
}
@Override
final Element element = runtime.toXml(runtimeBeans, configBeans, document);
- logger.info("{} operation successful", GET);
+ logger.info("{} operation successful", XmlNetconfConstants.GET);
return element;
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
import org.opendaylight.controller.config.yang.store.api.YangStoreException;
import org.opendaylight.controller.config.yang.store.api.YangStoreService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.yangtools.yang.model.api.Module;
-import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Manages life cycle of {@link YangStoreSnapshot}.
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
// [RFC6241] 8.5. Rollback-on-Error Capability
capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
- // [RFC6022] get-schema RPC. TODO: implement rest of the RFC
- capabilities.add(new BasicCapability("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04"));
final Collection<Map.Entry<Module, String>> modulesAndContents = yangStoreSnapshot.getModuleMap().values();
for (Map.Entry<Module, String> moduleAndContent : modulesAndContents) {
return capability;
}
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
@Override
public Optional<String> getModuleName() {
return Optional.absent();
public Optional<String> getCapabilitySchema() {
return Optional.absent();
}
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
}
private static class YangStoreCapability extends BasicCapability {
private final String content;
private final String revision;
private final String moduleName;
+ private final String moduleNamespace;
public YangStoreCapability(Map.Entry<Module, String> moduleAndContent) {
super(getAsString(moduleAndContent.getKey()));
this.content = moduleAndContent.getValue();
Module module = moduleAndContent.getKey();
this.moduleName = module.getName();
+ this.moduleNamespace = module.getNamespace().toString();
this.revision = Util.writeDate(module.getRevision());
}
return Optional.of(moduleName);
}
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
@Override
public Optional<String> getRevision() {
return Optional.of(revision);
-<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <capabilities>
- <capability>urn:ietf:params:netconf:base:1.0</capability>
- </capabilities>
-</hello>
+ <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <capabilities>
+ <capability>urn:ietf:params:netconf:base:1.0</capability>
+ </capabilities>
+ </hello>
--- /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">
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${yangtools.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ target/generated-sources/monitoring
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>maven-sal-api-gen-plugin</artifactId>
+ <version>${yangtools.binding.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/sal</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ com.google.common.collect,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yangtools.yang.binding,
+ org.opendaylight.yangtools.yang.common,
+ </Import-Package>
+ <Export-Package>
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+module ietf-netconf-monitoring {
+
+ yang-version 1;
+
+ namespace
+ "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+
+ prefix ncm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ WG Chair: Mehmet Ersue
+ <mailto:mehmet.ersue@nsn.com>
+
+ WG Chair: Bert Wijnen
+ <mailto:bertietf@bwijnen.net>
+
+ Editor: Mark Scott
+ <mailto:mark.scott@ericsson.com>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "NETCONF Monitoring Module.
+ All elements in this module are read-only.
+
+ Copyright (c) 2010 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6022; see
+ the RFC itself for full legal notices.";
+
+ revision "2010-10-04" {
+ description "Initial revision.";
+ reference
+ "RFC 6022: YANG Module for NETCONF Monitoring";
+
+ }
+
+
+ typedef netconf-datastore-type {
+ type enumeration {
+ enum "running" {
+ value 0;
+ }
+ enum "candidate" {
+ value 1;
+ }
+ enum "startup" {
+ value 2;
+ }
+ }
+ description
+ "Enumeration of possible NETCONF datastore types.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ identity transport {
+ description
+ "Base identity for NETCONF transport types.";
+ }
+
+ identity netconf-ssh {
+ base transport;
+ description
+ "NETCONF over Secure Shell (SSH).";
+ reference
+ "RFC 4742: Using the NETCONF Configuration Protocol
+ over Secure SHell (SSH)";
+
+ }
+
+ identity netconf-soap-over-beep {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP) over
+ Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+
+ }
+
+ identity netconf-soap-over-https {
+ base transport;
+ description
+ "NETCONF over Simple Object Access Protocol (SOAP)
+ over Hypertext Transfer Protocol Secure (HTTPS).";
+ reference
+ "RFC 4743: Using NETCONF over the Simple Object
+ Access Protocol (SOAP)";
+
+ }
+
+ identity netconf-beep {
+ base transport;
+ description
+ "NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
+ reference
+ "RFC 4744: Using the NETCONF Protocol over the
+ Blocks Extensible Exchange Protocol (BEEP)";
+
+ }
+
+ identity netconf-tls {
+ base transport;
+ description
+ "NETCONF over Transport Layer Security (TLS).";
+ reference
+ "RFC 5539: NETCONF over Transport Layer Security (TLS)";
+
+ }
+
+ identity schema-format {
+ description
+ "Base identity for data model schema languages.";
+ }
+
+ identity xsd {
+ base schema-format;
+ description
+ "W3C XML Schema Definition.";
+ reference
+ "W3C REC REC-xmlschema-1-20041028:
+ XML Schema Part 1: Structures";
+
+ }
+
+ identity yang {
+ base schema-format;
+ description
+ "The YANG data modeling language for NETCONF.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+
+ }
+
+ identity yin {
+ base schema-format;
+ description "The YIN syntax for YANG.";
+ reference
+ "RFC 6020: YANG - A Data Modeling Language for the
+ Network Configuration Protocol (NETCONF)";
+
+ }
+
+ identity rng {
+ base schema-format;
+ description
+ "Regular Language for XML Next Generation (RELAX NG).";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+
+ }
+
+ identity rnc {
+ base schema-format;
+ description "Relax NG Compact Syntax";
+ reference
+ "ISO/IEC 19757-2:2008: RELAX NG";
+
+ }
+
+ grouping common-counters {
+ description
+ "Counters that exist both per session, and also globally,
+ accumulated from all sessions.";
+ leaf in-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of correct <rpc> messages received.";
+ }
+
+ leaf in-bad-rpcs {
+ type yang:zero-based-counter32;
+ description
+ "Number of messages received when an <rpc> message was expected,
+ that were not correct <rpc> messages. This includes XML parse
+ errors and errors on the rpc layer.";
+ }
+
+ leaf out-rpc-errors {
+ type yang:zero-based-counter32;
+ description
+ "Number of <rpc-reply> messages sent that contained an
+ <rpc-error> element.";
+ }
+
+ leaf out-notifications {
+ type yang:zero-based-counter32;
+ description
+ "Number of <notification> messages sent.";
+ }
+ } // grouping common-counters
+
+ container netconf-state {
+ config false;
+ description
+ "The netconf-state container is the root of the monitoring
+ data model.";
+ container capabilities {
+ description
+ "Contains the list of NETCONF capabilities supported by the
+ server.";
+ leaf-list capability {
+ type inet:uri;
+ description
+ "List of NETCONF capabilities supported by the server.";
+ }
+ } // container capabilities
+
+ container datastores {
+ description
+ "Contains the list of NETCONF configuration datastores.";
+ list datastore {
+ key "name";
+ description
+ "List of NETCONF configuration datastores supported by
+ the NETCONF server and related information.";
+ leaf name {
+ type netconf-datastore-type;
+ description
+ "Name of the datastore associated with this list entry.";
+ }
+
+ container locks {
+ presence
+ "This container is present only if the datastore
+ is locked.";
+ description
+ "The NETCONF <lock> and <partial-lock> operations allow
+ a client to lock specific resources in a datastore. The
+ NETCONF server will prevent changes to the locked
+ resources by all sessions except the one that acquired
+ the lock(s).
+
+ Monitoring information is provided for each datastore
+ entry including details such as the session that acquired
+ the lock, the type of lock (global or partial) and the
+ list of locked resources. Multiple locks per datastore
+ are supported.";
+ grouping lock-info {
+ description
+ "Lock related parameters, common to both global and
+ partial locks.";
+ leaf locked-by-session {
+ type uint32;
+ mandatory true;
+ description
+ "The session ID of the session that has locked
+ this resource. Both a global lock and a partial
+ lock MUST contain the NETCONF session-id.
+
+ If the lock is held by a session that is not managed
+ by the NETCONF server (e.g., a CLI session), a session
+ id of 0 (zero) is reported.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ leaf locked-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The date and time of when the resource was
+ locked.";
+ }
+ } // grouping lock-info
+ choice lock-type {
+ description
+ "Indicates if a global lock or a set of partial locks
+ are set.";
+ container global-lock {
+ description
+ "Present if the global lock is set.";
+ uses lock-info;
+ } // container global-lock
+ list partial-lock {
+ key "lock-id";
+ description
+ "List of partial locks.";
+ reference
+ "RFC 5717: Partial Lock Remote Procedure Call (RPC) for
+ NETCONF";
+
+ leaf lock-id {
+ type uint32;
+ description
+ "This is the lock id returned in the <partial-lock>
+ response.";
+ }
+
+ uses lock-info;
+
+ leaf-list select {
+ type yang:xpath1.0;
+ min-elements 1;
+ description
+ "The xpath expression that was used to request
+ the lock. The select expression indicates the
+ original intended scope of the lock.";
+ }
+
+ leaf-list locked-node {
+ type instance-identifier;
+ description
+ "The list of instance-identifiers (i.e., the
+ locked nodes).
+
+ The scope of the partial lock is defined by the list
+ of locked nodes.";
+ }
+ } // list partial-lock
+ } // choice lock-type
+ } // container locks
+ } // list datastore
+ } // container datastores
+
+ container schemas {
+ description
+ "Contains the list of data model schemas supported by the
+ server.";
+ list schema {
+ key "identifier version format";
+ description
+ "List of data model schemas supported by the server.";
+ leaf identifier {
+ type string;
+ description
+ "Identifier to uniquely reference the schema. The
+ identifier is used in the <get-schema> operation and may
+ be used for other purposes such as file retrieval.
+
+ For modeling languages that support or require a data
+ model name (e.g., YANG module name) the identifier MUST
+ match that name. For YANG data models, the identifier is
+ the name of the module or submodule. In other cases, an
+ identifier such as a filename MAY be used instead.";
+ }
+
+ leaf version {
+ type string;
+ description
+ "Version of the schema supported. Multiple versions MAY be
+ supported simultaneously by a NETCONF server. Each
+ version MUST be reported individually in the schema list,
+ i.e., with same identifier, possibly different location,
+ but different version.
+
+ For YANG data models, version is the value of the most
+ recent YANG 'revision' statement in the module or
+ submodule, or the empty string if no 'revision' statement
+ is present.";
+ }
+
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language the schema is written
+ in (currently xsd, yang, yin, rng, or rnc).
+ For YANG data models, 'yang' format MUST be supported and
+ 'yin' format MAY also be provided.";
+ }
+
+ leaf namespace {
+ type inet:uri;
+ mandatory true;
+ description
+ "The XML namespace defined by the data model.
+
+ For YANG data models, this is the module's namespace.
+ If the list entry describes a submodule, this field
+ contains the namespace of the module to which the
+ submodule belongs.";
+ }
+
+ leaf-list location {
+ type union {
+ type enumeration {
+ enum "NETCONF" {
+ value 0;
+ }
+ }
+ type inet:uri;
+ }
+ description
+ "One or more locations from which the schema can be
+ retrieved. This list SHOULD contain at least one
+ entry per schema.
+
+ A schema entry may be located on a remote file system
+ (e.g., reference to file system for ftp retrieval) or
+ retrieved directly from a server supporting the
+ <get-schema> operation (denoted by the value 'NETCONF').";
+ }
+ } // list schema
+ } // container schemas
+
+ container sessions {
+ description
+ "The sessions container includes session-specific data for
+ NETCONF management sessions. The session list MUST include
+ all currently active NETCONF sessions.";
+ list session {
+ key "session-id";
+ description
+ "All NETCONF sessions managed by the NETCONF server
+ MUST be reported in this list.";
+ leaf session-id {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "Unique identifier for the session. This value is the
+ NETCONF session identifier, as defined in RFC 4741.";
+ reference
+ "RFC 4741: NETCONF Configuration Protocol";
+
+ }
+
+ leaf transport {
+ type identityref {
+ base transport;
+ }
+ mandatory true;
+ description
+ "Identifies the transport for each session, e.g.,
+ 'netconf-ssh', 'netconf-soap', etc.";
+ }
+
+ leaf username {
+ type string;
+ mandatory true;
+ description
+ "The username is the client identity that was authenticated
+ by the NETCONF transport protocol. The algorithm used to
+ derive the username is NETCONF transport protocol specific
+ and in addition specific to the authentication mechanism
+ used by the NETCONF transport protocol.";
+ }
+
+ leaf source-host {
+ type inet:host;
+ description
+ "Host identifier of the NETCONF client. The value
+ returned is implementation specific (e.g., hostname,
+ IPv4 address, IPv6 address)";
+ }
+
+ leaf login-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "Time at the server at which the session was established.";
+ }
+
+ uses common-counters {
+ description
+ "Per-session counters. Zero based with following reset
+ behaviour:
+ - at start of a session
+ - when max value is reached";
+ }
+ } // list session
+ } // container sessions
+
+ container statistics {
+ description
+ "Statistical data pertaining to the NETCONF server.";
+ leaf netconf-start-time {
+ type yang:date-and-time;
+ description
+ "Date and time at which the management subsystem was
+ started.";
+ }
+
+ leaf in-bad-hellos {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions silently dropped because an
+ invalid <hello> message was received. This includes <hello>
+ messages with a 'session-id' attribute, bad namespace, and
+ bad capability declarations.";
+ }
+
+ leaf in-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions started. This counter is incremented
+ when a <hello> message with a <session-id> is sent.
+
+ 'in-sessions' - 'in-bad-hellos' =
+ 'number of correctly started netconf sessions'";
+ }
+
+ leaf dropped-sessions {
+ type yang:zero-based-counter32;
+ description
+ "Number of sessions that were abnormally terminated, e.g.,
+ due to idle timeout or transport close. This counter is not
+ incremented when a session is properly closed by a
+ <close-session> operation, or killed by a <kill-session>
+ operation.";
+ }
+
+ uses common-counters {
+ description
+ "Global counters, accumulated from all sessions.
+ Zero based with following reset behaviour:
+ - re-initialization of NETCONF server
+ - when max value is reached";
+ }
+ } // container statistics
+ } // container netconf-state
+
+ rpc get-schema {
+ description
+ "This operation is used to retrieve a schema from the
+ NETCONF server.
+
+ Positive Response:
+ The NETCONF server returns the requested schema.
+
+ Negative Response:
+ If requested schema does not exist, the <error-tag> is
+ 'invalid-value'.
+
+ If more than one schema matches the requested parameters, the
+ <error-tag> is 'operation-failed', and <error-app-tag> is
+ 'data-not-unique'.";
+ input {
+ leaf identifier {
+ type string;
+ mandatory true;
+ description
+ "Identifier for the schema list entry.";
+ }
+
+ leaf version {
+ type string;
+ description
+ "Version of the schema requested. If this parameter is not
+ present, and more than one version of the schema exists on
+ the server, a 'data-not-unique' error is returned, as
+ described above.";
+ }
+
+ leaf format {
+ type identityref {
+ base schema-format;
+ }
+ description
+ "The data modeling language of the schema. If this
+ parameter is not present, and more than one formats of
+ the schema exists on the server, a 'data-not-unique' error
+ is returned, as described above.";
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Contains the schema content.";
+ }
+ }
+ } // rpc get-schema
+} // module
\ No newline at end of file
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>framework</artifactId>
io.netty.channel,
io.netty.util.concurrent,
org.w3c.dom,
- org.slf4j
+ org.slf4j,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ com.google.common.base,
</Import-Package>
<Export-Package>
org.opendaylight.controller.netconf.api,
org.opendaylight.controller.netconf.api.jmx,
+ org.opendaylight.controller.netconf.api.monitoring,
</Export-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.api;
+import com.google.common.base.Optional;
import org.w3c.dom.Document;
/**
private final Document doc;
+ private String additionalHeader;
+
public NetconfMessage(final Document doc) {
+ this(doc, null);
+ }
+
+ public NetconfMessage(Document doc, String additionalHeader) {
this.doc = doc;
+ this.additionalHeader = additionalHeader;
}
public Document getDocument() {
return this.doc;
}
+
+ public Optional<String> getAdditionalHeader() {
+ return additionalHeader== null ? Optional.<String>absent() : Optional.of(additionalHeader);
+ }
}
@Override
public void close() {
channel.close();
+ up = false;
sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed"));
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.api.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+public interface NetconfManagementSession {
+
+ Session toManagementSession();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.api.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+
+public interface NetconfMonitoringService {
+
+ Sessions getSessions();
+
+ Schemas getSchemas();
+}
public Collection<String> getServerCapabilities() {
return capabilities;
}
+
}
import io.netty.channel.Channel;
import io.netty.util.Timer;
import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
}
@Override
- protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
- return new NetconfClientSession(sessionListener, channel, extractSessionId(doc), getCapabilities(doc));
+ protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, NetconfMessage message) {
+ return new NetconfClientSession(sessionListener, channel, extractSessionId(message.getDocument()),
+ getCapabilities(message.getDocument()));
}
}
<groupId>${project.groupId}</groupId>
<artifactId>netconf-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-util</artifactId>
<artifactId>netconf-mapping-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.opendaylight.bgpcep</groupId>
<artifactId>util</artifactId>
io.netty.buffer,
io.netty.handler.codec,
io.netty.channel.nio,
+ javax.annotation,
javax.management,
javax.net.ssl,
javax.xml.namespace,
org.w3c.dom,
org.xml.sax,
org.opendaylight.controller.netconf.util.messages,
- com.siemens.ct.exi.exceptions
+ com.siemens.ct.exi.exceptions,
+ io.netty.util.internal,
+ org.opendaylight.controller.netconf.api.monitoring,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
</Import-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.impl;
+import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
-
import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
+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.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NetconfServerSession extends NetconfSession {
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NetconfServerSession extends NetconfSession implements NetconfManagementSession {
private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
- public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) {
- super(sessionListener,channel,sessionId);
+ private final NetconfServerSessionNegotiator.AdditionalHeader header;
+
+ private Date loginTime;
+ private long inRpcSuccess, inRpcFail, outRpcError;
+
+ public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId,
+ NetconfServerSessionNegotiator.AdditionalHeader header) {
+ super(sessionListener, channel, sessionId);
+ this.header = header;
logger.debug("Session {} created", toString());
}
+
+ @Override
+ protected void sessionUp() {
+ super.sessionUp();
+ Preconditions.checkState(loginTime == null, "Session is already up");
+ this.loginTime = new Date();
+ }
+
+ public void onIncommingRpcSuccess() {
+ inRpcSuccess++;
+ }
+
+ public void onIncommingRpcFail() {
+ inRpcFail++;
+ }
+
+ public void onOutgoingRpcError() {
+ outRpcError++;
+ }
+
+ public static final String ISO_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+
+ @Override
+ public Session toManagementSession() {
+ SessionBuilder builder = new SessionBuilder();
+
+ builder.setSessionId(getSessionId());
+ builder.setSourceHost(new Host(new DomainName(header.getAddress())));
+
+ Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
+ String formattedDateTime = formatDateTime(loginTime);
+ String pattern = DateAndTime.PATTERN_CONSTANTS.get(0);
+ Matcher matcher = Pattern.compile(pattern).matcher(formattedDateTime);
+ Preconditions.checkState(matcher.matches(), "Formatted datetime %s does not match pattern %s", formattedDateTime, pattern);
+ builder.setLoginTime(new DateAndTime(formattedDateTime));
+
+ builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
+ builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
+ builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));
+
+ builder.setUsername(header.getUsername());
+ builder.setTransport(getTransportForString(header.getTransport()));
+
+ builder.setOutNotifications(new ZeroBasedCounter32(0L));
+
+ builder.setKey(new SessionKey(getSessionId()));
+ return builder.build();
+ }
+
+ private Class<? extends Transport> getTransportForString(String transport) {
+ switch(transport) {
+ case "ssh" : return NetconfSsh.class;
+ // TODO what about tcp
+ case "tcp" : return NetconfSsh.class;
+ default: throw new IllegalArgumentException("Unknown transport type " + transport);
+ }
+ }
+
+ private String formatDateTime(Date loginTime) {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_DATE_FORMAT);
+ return dateFormat.format(loginTime);
+ }
+
}
package org.opendaylight.controller.netconf.impl;
-import static com.google.common.base.Preconditions.checkState;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
+import static com.google.common.base.Preconditions.checkState;
public class NetconfServerSessionListener implements
SessionListener<NetconfMessage, NetconfServerSession, NetconfTerminationReason> {
static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
public static final String MESSAGE_ID = "message-id";
+ private final SessionMonitoringService monitoringService;
private NetconfOperationRouterImpl operationRouter;
- public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+ public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter,
+ SessionMonitoringService monitoringService) {
this.operationRouter = operationRouter;
+ this.monitoringService = monitoringService;
}
@Override
public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
-
+ monitoringService.onSessionUp(netconfNetconfServerSession);
}
@Override
public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+ monitoringService.onSessionDown(netconfNetconfServerSession);
operationRouter.close();
}
NetconfTerminationReason netconfTerminationReason) {
logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
netconfTerminationReason.getErrorMessage());
+ monitoringService.onSessionDown(netconfNetconfServerSession);
operationRouter.close();
}
// schemas
final NetconfMessage message = processDocument(netconfMessage,
session);
- logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument()));
+ logger.debug("Responding with message {}", XmlUtil.toString(message.getDocument()));
session.sendMessage(message);
if (isCloseSession(netconfMessage)) {
}
} catch (final RuntimeException e) {
- logger.error("Unexpected exception", e);
// TODO: should send generic error or close session?
+ logger.error("Unexpected exception", e);
+ session.onIncommingRpcFail();
throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
} catch (NetconfDocumentedException e) {
+ session.onOutgoingRpcError();
+ session.onIncommingRpcFail();
SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
}
}
}
private NetconfMessage processDocument(final NetconfMessage netconfMessage,
- NetconfSession session) throws NetconfDocumentedException {
+ NetconfServerSession session) throws NetconfDocumentedException {
final Document incommingDocument = netconfMessage.getDocument();
final Node rootNode = incommingDocument.getDocumentElement();
final Document responseDocument = XmlUtil.newDocument();
Document rpcReply = operationRouter.onNetconfMessage(
incommingDocument, session);
+
+ session.onIncommingRpcSuccess();
+
responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
return new NetconfMessage(responseDocument);
} else {
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.protocol.framework.SessionListenerFactory;
public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
private final SessionIdProvider idProvider;
+ private final SessionMonitoringService monitor;
+
public NetconfServerSessionListenerFactory(NetconfOperationServiceFactoryListener factoriesListener,
- DefaultCommitNotificationProducer commitNotifier,
- SessionIdProvider idProvider) {
+ DefaultCommitNotificationProducer commitNotifier,
+ SessionIdProvider idProvider, SessionMonitoringService monitor) {
this.factoriesListener = factoriesListener;
this.commitNotifier = commitNotifier;
this.idProvider = idProvider;
+ this.monitor = monitor;
}
@Override
netconfOperationServiceSnapshot, capabilityProvider,
commitNotifier);
- return new NetconfServerSessionListener(operationRouter);
+ return new NetconfServerSessionListener(operationRouter, monitor);
}
}
package org.opendaylight.controller.netconf.impl;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.netty.channel.Channel;
+import io.netty.util.Timer;
+import io.netty.util.concurrent.Promise;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator;
import org.opendaylight.protocol.framework.SessionListener;
-import org.w3c.dom.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import io.netty.channel.Channel;
-import io.netty.util.Timer;
-import io.netty.util.concurrent.Promise;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class NetconfServerSessionNegotiator extends
AbstractNetconfSessionNegotiator<NetconfServerSessionPreferences, NetconfServerSession> {
+ static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiator.class);
+
+ private static final AdditionalHeader DEFAULT_HEADER = new AdditionalHeader();
+
protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences,
Promise<NetconfServerSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
super(sessionPreferences, promise, channel, timer, sessionListener);
}
@Override
- protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
- return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId());
+ protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, NetconfMessage message) {
+ Optional<String> additionalHeader = message.getAdditionalHeader();
+
+ AdditionalHeader parsedHeader;
+ if (additionalHeader.isPresent()) {
+ parsedHeader = new AdditionalHeader(additionalHeader.get());
+ } else {
+ parsedHeader = DEFAULT_HEADER;
+ }
+ logger.debug("Additional header from hello parsed as {} from {}", parsedHeader, additionalHeader);
+
+ return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId(), parsedHeader);
}
+ static class AdditionalHeader {
+
+ private static final Pattern pattern = Pattern
+ .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
+ private final String username;
+ private final String address;
+ private final String transport;
+
+ public AdditionalHeader(String addHeaderAsString) {
+ addHeaderAsString = addHeaderAsString.trim();
+ Matcher matcher = pattern.matcher(addHeaderAsString);
+ Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s",
+ addHeaderAsString, pattern);
+ this.username = matcher.group("username");
+ this.address = matcher.group("address");
+ this.transport = matcher.group("transport");
+ }
+
+ private AdditionalHeader() {
+ this.username = this.address = "unknown";
+ this.transport = "ssh";
+ }
+
+ String getUsername() {
+ return username;
+ }
+
+ String getAddress() {
+ return address;
+ }
+
+ String getTransport() {
+ return transport;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("AdditionalHeader{");
+ sb.append("username='").append(username).append('\'');
+ sb.append(", address='").append(address).append('\'');
+ sb.append(", transport='").append(transport).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+ }
}
package org.opendaylight.controller.netconf.impl.mapping.operations;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
import java.util.HashMap;
import java.util.Map;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
*/\r
package org.opendaylight.controller.netconf.impl.mapping.operations;\r
\r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
*/\r
package org.opendaylight.controller.netconf.impl.mapping.operations;\r
\r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
public class NetconfImplActivator implements BundleActivator {
private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class);
private NetconfServerDispatcher dispatch;
private NioEventLoopGroup eventLoopGroup;
private HashedWheelTimer timer;
+ private ServiceRegistration<NetconfMonitoringService> regMonitoring;
@Override
public void start(final BundleContext context) throws Exception {
"TCP is not configured, netconf not available.", false);
NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
- factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
- factoriesTracker.open();
+ startOperationServiceFactoryTracker(context, factoriesListener);
SessionIdProvider idProvider = new SessionIdProvider();
timer = new HashedWheelTimer();
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+ NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener);
+
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, monitoringService);
eventLoopGroup = new NioEventLoopGroup();
}
+ private void startOperationServiceFactoryTracker(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener);
+ factoriesTracker.open();
+ }
+
+ private NetconfMonitoringServiceImpl startMonitoringService(BundleContext context, NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ NetconfMonitoringServiceImpl netconfMonitoringServiceImpl = new NetconfMonitoringServiceImpl(factoriesListener);
+ Dictionary<String, ?> dic = new Hashtable<>();
+ regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
+
+ return netconfMonitoringServiceImpl;
+ }
+
@Override
public void stop(final BundleContext context) throws Exception {
logger.info("Shutting down netconf because YangStoreService service was removed");
commitNot.close();
eventLoopGroup.shutdownGracefully();
timer.stop();
+
+ regMonitoring.unregister();
+ factoriesTracker.close();
}
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.netty.util.internal.ConcurrentSet;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.SchemaKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class NetconfMonitoringServiceImpl implements NetconfMonitoringService, SessionMonitoringService {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceImpl.class);
+
+ private final Set<NetconfManagementSession> sessions = new ConcurrentSet<>();
+ private final NetconfOperationServiceFactoryListener factoriesListener;
+
+ public NetconfMonitoringServiceImpl(NetconfOperationServiceFactoryListener factoriesListener) {
+ this.factoriesListener = factoriesListener;
+ }
+
+ @Override
+ public void onSessionUp(NetconfManagementSession session) {
+ logger.debug("Session {} up", session);
+ Preconditions.checkState(sessions.contains(session) == false, "Session %s was already added", session);
+ sessions.add(session);
+ }
+
+ @Override
+ public void onSessionDown(NetconfManagementSession session) {
+ logger.debug("Session {} down", session);
+ Preconditions.checkState(sessions.contains(session) == true, "Session %s not present", session);
+ sessions.remove(session);
+ }
+
+ @Override
+ public Sessions getSessions() {
+ return new SessionsBuilder().setSession(transformSessions(sessions)).build();
+ }
+
+ @Override
+ public Schemas getSchemas() {
+ // FIXME, session ID
+ return transformSchemas(factoriesListener.getSnapshot(0));
+ }
+
+ private Schemas transformSchemas(NetconfOperationServiceSnapshot snapshot) {
+ Set<Capability> caps = Sets.newHashSet();
+
+ List<Schema> schemas = Lists.newArrayList();
+
+ for (NetconfOperationService netconfOperationService : snapshot.getServices()) {
+ // TODO check for duplicates ? move capability merging to snapshot
+ caps.addAll(netconfOperationService.getCapabilities());
+ }
+
+ for (Capability cap : caps) {
+ SchemaBuilder builder = new SchemaBuilder();
+
+ if(cap.getCapabilitySchema().isPresent() == false)
+ continue;
+
+ Preconditions.checkState(cap.getModuleNamespace().isPresent());
+ builder.setNamespace(new Uri(cap.getModuleNamespace().get()));
+
+ Preconditions.checkState(cap.getRevision().isPresent());
+ String version = cap.getRevision().get();
+ builder.setVersion(version);
+
+ Preconditions.checkState(cap.getModuleName().isPresent());
+ String identifier = cap.getModuleName().get();
+ builder.setIdentifier(identifier);
+
+ builder.setFormat(Yang.class);
+
+ builder.setLocation(transformLocations(cap.getLocation().or(Collections.<String> emptyList())));
+
+ builder.setKey(new SchemaKey(Yang.class, identifier, version));
+
+ schemas.add(builder.build());
+ }
+
+ return new SchemasBuilder().setSchema(schemas).build();
+ }
+
+ private List<Schema.Location> transformLocations(List<String> locations) {
+ List<Schema.Location> monitoringLocations = Lists.newArrayList();
+ monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.NETCONF));
+
+ for (String location : locations) {
+ // TODO how to create enumerration from string location ?
+ // monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.valueOf(location)));
+ }
+
+ return monitoringLocations;
+ }
+
+ private List<Session> transformSessions(Set<NetconfManagementSession> sessions) {
+ return Lists.newArrayList(Collections2.transform(sessions, new Function<NetconfManagementSession, Session>() {
+ @Nullable
+ @Override
+ public Session apply(@Nullable NetconfManagementSession input) {
+ return input.toManagementSession();
+ }
+ }));
+ }
+}
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
- CapabilityProvider capabilityProvider,
- DefaultCommitNotificationProducer commitNotifier) {
+ CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot;
.getNetconfSessionIdForReporting()));
defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot
.getNetconfSessionIdForReporting()));
- defaultNetconfOperations.add(new DefaultStartExi(
- netconfOperationServiceSnapshot
- .getNetconfSessionIdForReporting()));
- defaultNetconfOperations.add(new DefaultStopExi(
- netconfOperationServiceSnapshot
- .getNetconfSessionIdForReporting()));
+ defaultNetconfOperations.add(new DefaultStartExi(netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
+ defaultNetconfOperations.add(new DefaultStopExi(netconfOperationServiceSnapshot
+ .getNetconfSessionIdForReporting()));
allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
final Set<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
for (NetconfOperationFilter filter : filtersFromService) {
- Preconditions.checkState(result.contains(filter) == false, "Filter %s already present", filter);
+ Preconditions.checkState(result.contains(filter) == false,
+ "Filter %s already present, all filters so far: %s", filter, result);
result.add(filter);
}
}
@Override
public synchronized Document onNetconfMessage(Document message,
NetconfSession session) throws NetconfDocumentedException {
- NetconfOperationExecution netconfOperationExecution = null;
+ NetconfOperationExecution netconfOperationExecution;
String messageAsString = XmlUtil.toString(message);
class NetconfOperationServiceFactoryTracker extends
ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
- private final NetconfOperationServiceFactoryListener operationRouter;
+ private final NetconfOperationServiceFactoryListener factoriesListener;
NetconfOperationServiceFactoryTracker(BundleContext context,
- final NetconfOperationServiceFactoryListener operationRouter) {
+ final NetconfOperationServiceFactoryListener factoriesListener) {
super(context, NetconfOperationServiceFactory.class, null);
- this.operationRouter = operationRouter;
+ this.factoriesListener = factoriesListener;
}
@Override
public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
- operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
return netconfOperationServiceFactory;
}
@Override
public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
NetconfOperationServiceFactory netconfOperationServiceFactory) {
- operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+ factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
}
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.impl.osgi;
+
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+
+public interface SessionMonitoringService {
+
+ void onSessionUp(NetconfManagementSession session);
+
+ void onSessionDown(NetconfManagementSession session);
+}
package org.opendaylight.controller.netconf.impl.util;
-import java.util.Map;
-
-import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
+import com.google.common.collect.Maps;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import com.google.common.collect.Maps;
+import java.util.Map;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
+public final class
+ DeserializerExceptionHandler implements ChannelHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeserializerExceptionHandler.class);
-public final class DeserializerExceptionHandler implements ChannelHandler {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- if (cause instanceof NetconfDeserializerException) {
- handleDeserializerException(ctx, cause);
- }
+ logger.warn("An exception occured during message handling", cause);
+ handleDeserializerException(ctx, cause);
}
private void handleDeserializerException(ChannelHandlerContext ctx, Throwable cause) {
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.impl;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+public class AdditionalHeaderParserTest {
+
+ @Test
+ public void testParsing() throws Exception {
+ String s = "[netconf;10.12.0.102:48528;ssh;;;;;;]";
+ NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ Assert.assertEquals("netconf", header.getUsername());
+ Assert.assertEquals("10.12.0.102", header.getAddress());
+ Assert.assertEquals("ssh", header.getTransport());
+ }
+
+ @Test
+ public void testParsing2() throws Exception {
+ String s = "[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]";
+ NetconfServerSessionNegotiator.AdditionalHeader header = new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ Assert.assertEquals("tomas", header.getUsername());
+ Assert.assertEquals("10.0.0.0", header.getAddress());
+ Assert.assertEquals("tcp", header.getTransport());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParsingNoUsername() throws Exception {
+ String s = "[10.12.0.102:48528;ssh;;;;;;]";
+ new NetconfServerSessionNegotiator.AdditionalHeader(s);
+ }
+}
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.io.DataOutputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import javax.management.ObjectName;
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.opendaylight.controller.netconf.client.NetconfClient;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
+
+import javax.management.ObjectName;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
import static com.google.common.base.Preconditions.checkNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
private DefaultCommitNotificationProducer commitNot;
private NetconfServerDispatcher dispatch;
+ @Mock
+ private SessionMonitoringService monitoring;
@Before
public void setUp() throws Exception {
commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+ doNothing().when(monitoring).onSessionUp(any(NetconfServerSession.class));
+ doNothing().when(monitoring).onSessionDown(any(NetconfServerSession.class));
+
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, monitoring);
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory, listenerFactory);
dispatch = new NetconfServerDispatcher(serverChannelInitializer, nettyGroup, nettyGroup);
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+
public class NetconfDispatcherImplTest {
private EventLoopGroup nettyGroup;
new HashedWheelTimer(), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, null);
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory, listenerFactory);
<artifactId>netconf-impl</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-mapping-api</artifactId>
new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, NetconfITTest.getNetconfMonitoringListenerService());
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
serverNegotiatorFactory, listenerFactory);
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.management.ManagementFactory;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Pattern;
-import javax.management.ObjectName;
-import javax.xml.parsers.ParserConfigurationException;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.opendaylight.controller.netconf.impl.SessionIdProvider;
import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;
import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
+
+import javax.management.ObjectName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
import static java.util.Collections.emptyList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
public class NetconfITTest extends AbstractConfigTest {
- private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
- //
+ // TODO refactor, pull common code up to AbstractNetconfITTest
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
private static final InetSocketAddress sshAddress = new InetSocketAddress("127.0.0.1", 10830);
private NetconfClientDispatcher clientDispatcher;
-
@Before
public void setUp() throws Exception {
super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray(
new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
- factoriesListener, commitNot, idProvider);
+ factoriesListener, commitNot, idProvider, getNetconfMonitoringListenerService());
NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
serverNegotiatorFactory, listenerFactory);
return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
}
+ static NetconfMonitoringServiceImpl getNetconfMonitoringListenerService() {
+ NetconfOperationServiceFactoryListener factoriesListener = mock(NetconfOperationServiceFactoryListener.class);
+ NetconfOperationServiceSnapshot snap = mock(NetconfOperationServiceSnapshot.class);
+ doReturn(Collections.<NetconfOperationService>emptySet()).when(snap).getServices();
+ doReturn(snap).when(factoriesListener).getSnapshot(anyLong());
+ return new NetconfMonitoringServiceImpl(factoriesListener);
+ }
+
@After
public void tearDown() throws Exception {
commitNot.close();
assertEquals("ok", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName());
}
- @Ignore
- @Test
- // TODO can only send NetconfMessage - it must be valid xml
- public void testClientHelloWithAuth() throws Exception {
- final String fileName = "netconfMessages/client_hello_with_auth.xml";
- // final InputStream resourceAsStream =
- // AbstractListenerTest.class.getResourceAsStream(fileName);
- // assertNotNull(resourceAsStream);
- try (NetconfClient netconfClient = new NetconfClient("test", tcpAddress, 5000, clientDispatcher)) {
- // IOUtils.copy(resourceAsStream, netconfClient.getStream());
- // netconfClient.getOutputStream().write(NetconfMessageFactory.endOfMessage);
- // server should not write anything back
- // assertEquals(null, netconfClient.readMessage());
- assertGetConfigWorks(netconfClient);
- }
- }
-
private Document assertGetConfigWorks(final NetconfClient netconfClient) throws InterruptedException {
return assertGetConfigWorks(netconfClient, this.getConfig);
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.it;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.mockito.Mock;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.client.NetconfClient;
+import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
+import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
+import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class NetconfMonitoringITTest extends AbstractConfigTest {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfITTest.class);
+
+ private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023);
+
+ @Mock
+ private DefaultCommitNotificationProducer commitNot;
+ private NetconfServerDispatcher dispatch;
+ private EventLoopGroup nettyThreadgroup;
+
+ private NetconfClientDispatcher clientDispatcher;
+
+ private NetconfMonitoringServiceImpl monitoringService;
+
+ @Before
+ public void setUp() throws Exception {
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(NetconfITTest.getModuleFactoriesS().toArray(
+ new ModuleFactory[0])));
+
+ monitoringService = new NetconfMonitoringServiceImpl(getFactoriesListener());
+
+ NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl();
+ factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore()));
+ factoriesListener
+ .onAddNetconfOperationServiceFactory(new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ new NetconfMonitoringOperationService(monitoringService)));
+
+ nettyThreadgroup = new NioEventLoopGroup();
+
+ dispatch = createDispatcher(factoriesListener);
+ ChannelFuture s = dispatch.createServer(tcpAddress);
+ s.await();
+
+ clientDispatcher = new NetconfClientDispatcher(nettyThreadgroup, nettyThreadgroup);
+ }
+
+ private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException {
+ final Collection<InputStream> yangDependencies = NetconfITTest.getBasicYangs();
+ return new HardcodedYangStoreService(yangDependencies);
+ }
+
+ private NetconfServerDispatcher createDispatcher(
+ NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+ SessionIdProvider idProvider = new SessionIdProvider();
+ NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+ NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+ factoriesListener, commitNot, idProvider, getNetconfMonitoringListenerService(logger, monitoringService));
+
+ NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ serverNegotiatorFactory, listenerFactory);
+ return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+ }
+
+ static SessionMonitoringService getNetconfMonitoringListenerService(final Logger logger, final NetconfMonitoringServiceImpl monitor) {
+ return new SessionMonitoringService() {
+ @Override
+ public void onSessionUp(NetconfManagementSession session) {
+ logger.debug("Management session up {}", session);
+ monitor.onSessionUp(session);
+ }
+
+ @Override
+ public void onSessionDown(NetconfManagementSession session) {
+ logger.debug("Management session down {}", session);
+ monitor.onSessionDown(session);
+ }
+ };
+ }
+
+
+ @Test
+ public void testGetResponseFromMonitoring() throws Exception {
+ try (NetconfClient netconfClient = new NetconfClient("client-monitoring", tcpAddress, 4000, clientDispatcher)) {
+ try (NetconfClient netconfClient2 = new NetconfClient("client-monitoring2", tcpAddress, 4000, clientDispatcher)) {
+ NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+ assertSessionElementsInResponse(response.getDocument(), 2);
+ }
+ NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+ assertSessionElementsInResponse(response.getDocument(), 1);
+ }
+ }
+
+
+ @Test(timeout = 5 * 10000)
+ public void testClientHelloWithAuth() throws Exception {
+ String fileName = "netconfMessages/client_hello_with_auth.xml";
+ String hello = XmlFileLoader.fileToString(fileName);
+
+ fileName = "netconfMessages/get.xml";
+ String get = XmlFileLoader.fileToString(fileName);
+
+ Socket sock = new Socket(tcpAddress.getHostName(), tcpAddress.getPort());
+ sock.getOutputStream().write(hello.getBytes(Charsets.UTF_8));
+ String separator = "]]>]]>";
+
+ sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+ sock.getOutputStream().write(get.getBytes(Charsets.UTF_8));
+ sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+
+ StringBuilder responseBuilder = new StringBuilder();
+
+ try (InputStream inputStream = sock.getInputStream();
+ InputStreamReader reader = new InputStreamReader(inputStream);
+ BufferedReader buff = new BufferedReader(reader)) {
+ String line;
+ while ((line = buff.readLine()) != null) {
+
+ responseBuilder.append(line);
+ responseBuilder.append(System.lineSeparator());
+ System.out.println(responseBuilder.toString());
+
+ if(line.contains("</rpc-reply>"))
+ break;
+ }
+ }
+
+ org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>"));
+ org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<username>tomas</username>"));
+ }
+
+ private void assertSessionElementsInResponse(Document document, int i) {
+ int elementSize = document.getElementsByTagName("session-id").getLength();
+ Assert.assertEquals(i, elementSize);
+ }
+
+ private NetconfMessage loadGetMessage() throws Exception {
+ return XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/get.xml");
+ }
+
+ public NetconfOperationServiceFactoryListener getFactoriesListener() {
+ NetconfOperationServiceFactoryListener factoriesListener = mock(NetconfOperationServiceFactoryListener.class);
+ NetconfOperationServiceSnapshot snap = mock(NetconfOperationServiceSnapshot.class);
+ NetconfOperationService service = mock(NetconfOperationService.class);
+ Set<Capability> caps = Sets.newHashSet();
+ caps.add(new Capability() {
+ @Override
+ public String getCapabilityUri() {
+ return "namespaceModuleRevision";
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of("namespace");
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of("name");
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of("revision");
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.of("content");
+ }
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
+ });
+
+ doReturn(caps).when(service).getCapabilities();
+ Set<NetconfOperationService> services = Sets.newHashSet(service);
+ doReturn(services).when(snap).getServices();
+ doReturn(snap).when(factoriesListener).getSnapshot(anyLong());
+
+ return factoriesListener;
+ }
+
+
+}
import com.google.common.base.Optional;
+import java.util.List;
+
/**
* Contains capability URI announced by server hello message and optionally its
* corresponding yang schema that can be retrieved by get-schema rpc.
public String getCapabilityUri();
+ public Optional<String> getModuleNamespace();
+
public Optional<String> getModuleName();
public Optional<String> getRevision();
public Optional<String> getCapabilitySchema();
+
+ public Optional<List<String>> getLocation();
}
--- /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">
+ <parent>
+ <artifactId>netconf-subsystem</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>0.2.3-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>netconf-monitoring</artifactId>
+ <name>${project.artifactId}</name>
+ <packaging>bundle</packaging>
+
+
+ <dependencies>
+ <!-- compile dependencies -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.bgpcep</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <version>${bgpcep.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Activator>org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator</Bundle-Activator>
+ <Import-Package>
+ com.google.common.base,
+ com.google.common.collect,
+ com.google.common.io,
+ org.opendaylight.controller.netconf.api,
+ org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.controller.netconf.util.mapping,
+ org.osgi.framework,
+ org.slf4j,
+ org.w3c.dom,
+ javax.xml.bind,
+ javax.xml.bind.annotation,
+ javax.xml.transform,
+ javax.xml.transform.dom,
+ org.opendaylight.controller.netconf.util.xml,
+ io.netty.util.internal,
+ javax.annotation,
+ org.opendaylight.controller.netconf.api.monitoring,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+ org.osgi.util.tracker,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
+ </Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring;
+
+import com.google.common.collect.Maps;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
+import org.opendaylight.controller.netconf.monitoring.xml.JaxBSerializer;
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.Map;
+
+public class Get implements NetconfOperationFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(Get.class);
+ private final NetconfMonitoringService netconfMonitor;
+
+ public Get(NetconfMonitoringService netconfMonitor) {
+ this.netconfMonitor = netconfMonitor;
+ }
+
+ @Override
+ public Document doFilter(Document message, NetconfOperationRouter operationRouter,
+ NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+ AbstractNetconfOperation.OperationNameAndNamespace operationNameAndNamespace = new AbstractNetconfOperation.OperationNameAndNamespace(
+ message);
+ if (canHandle(operationNameAndNamespace)) {
+ return handle(message, operationRouter, filterChain);
+ }
+ return filterChain.execute(message, operationRouter);
+ }
+
+ private Document handle(Document message, NetconfOperationRouter operationRouter,
+ NetconfOperationFilterChain filterChain) throws NetconfDocumentedException {
+ try {
+ Document innerResult = filterChain.execute(message, operationRouter);
+
+ NetconfState netconfMonitoring = new NetconfState(netconfMonitor);
+ Element monitoringXmlElement = new JaxBSerializer().toXml(netconfMonitoring);
+
+ monitoringXmlElement = (Element) innerResult.importNode(monitoringXmlElement, true);
+ Element monitoringXmlElementPlaceholder = getPlaceholder(innerResult);
+ monitoringXmlElementPlaceholder.appendChild(monitoringXmlElement);
+
+ return innerResult;
+ } catch (RuntimeException e) {
+ String errorMessage = "Get operation for netconf-state subtree failed";
+ logger.warn(errorMessage, e);
+ Map<String, String> info = Maps.newHashMap();
+ info.put(NetconfDocumentedException.ErrorSeverity.error.toString(), e.getMessage());
+ throw new NetconfDocumentedException(errorMessage, NetconfDocumentedException.ErrorType.application,
+ NetconfDocumentedException.ErrorTag.operation_failed,
+ NetconfDocumentedException.ErrorSeverity.error, info);
+ }
+ }
+
+ private Element getPlaceholder(Document innerResult) {
+ try {
+ XmlElement rootElement = XmlElement.fromDomElementWithExpected(innerResult.getDocumentElement(),
+ XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.RFC4741_TARGET_NAMESPACE);
+ return rootElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY).getDomElement();
+ } catch (RuntimeException e) {
+ throw new IllegalArgumentException(String.format(
+ "Input xml in wrong format, Expecting root element %s with child element %s, but was %s",
+ XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.DATA_KEY,
+ XmlUtil.toString(innerResult.getDocumentElement())), e);
+ }
+ }
+
+ private boolean canHandle(AbstractNetconfOperation.OperationNameAndNamespace operationNameAndNamespace) {
+ if (operationNameAndNamespace.getOperationName().equals(XmlNetconfConstants.GET) == false)
+ return false;
+ return operationNameAndNamespace.getNamespace().equals(
+ XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ }
+
+ @Override
+ public int getSortingOrder() {
+ // FIXME filters for different operations cannot have same order
+ return 1;
+ }
+
+ @Override
+ public int compareTo(NetconfOperationFilter o) {
+ return Integer.compare(getSortingOrder(), o.getSortingOrder());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring;
+
+public class MonitoringConstants {
+
+ public static final String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
+ public static final String MODULE_NAME = "ietf-netconf-monitoring";
+ public static final String MODULE_REVISION = "2010-10-04";
+
+ public static final String URI = String.format("%s?module=%s&revision=%s", NAMESPACE, MODULE_NAME, MODULE_REVISION);
+
+ public static final String NETCONF_MONITORING_XML_ROOT_ELEMENT = "netconf-state";
+}
--- /dev/null
+/*
+* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
+package org.opendaylight.controller.netconf.monitoring.osgi;
+
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfMonitoringActivator implements BundleActivator {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringActivator.class);
+
+ private NetconfMonitoringServiceTracker monitor;
+
+ @Override
+ public void start(final BundleContext context) throws Exception {
+ monitor = new NetconfMonitoringServiceTracker(context);
+ monitor.open();
+
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ if(monitor!=null) {
+ try {
+ monitor.close();
+ } catch (Exception e) {
+ logger.warn("Ignoring exception while closing {}", monitor, e);
+ }
+ }
+ }
+
+ public static class NetconfMonitoringOperationServiceFactory implements NetconfOperationServiceFactory {
+ private final NetconfMonitoringOperationService operationService;
+
+ public NetconfMonitoringOperationServiceFactory(NetconfMonitoringOperationService operationService) {
+ this.operationService = operationService;
+ }
+
+ @Override
+ public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) {
+ return operationService;
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
+package org.opendaylight.controller.netconf.monitoring.osgi;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.monitoring.Get;
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class NetconfMonitoringOperationService implements NetconfOperationService {
+
+ public static final HashSet<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+
+ @Override
+ public String getCapabilityUri() {
+ return MonitoringConstants.URI;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(MonitoringConstants.NAMESPACE);
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(MonitoringConstants.MODULE_NAME);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(MonitoringConstants.MODULE_REVISION);
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
+ });
+
+ private final NetconfMonitoringService monitor;
+
+ public NetconfMonitoringOperationService(NetconfMonitoringService monitor) {
+ this.monitor = monitor;
+ }
+
+ private static String readSchema() {
+ String schemaLocation = "/META-INF/yang/ietf-netconf-monitoring.yang";
+ URL resource = Schemas.class.getClassLoader().getResource(schemaLocation);
+ Preconditions.checkNotNull(resource, "Unable to read schema content from %s", schemaLocation);
+ File file = new File(resource.getFile());
+ try {
+ return Files.toString(file, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load schema from " + schemaLocation, e);
+ }
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<NetconfOperationFilter> getFilters() {
+ return Sets.<NetconfOperationFilter>newHashSet(new Get(monitor));
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring.osgi;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+public class NetconfMonitoringServiceTracker extends ServiceTracker<NetconfMonitoringService, NetconfMonitoringService> {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceTracker.class);
+
+ private ServiceRegistration<NetconfOperationServiceFactory> reg;
+
+ NetconfMonitoringServiceTracker(BundleContext context) {
+ super(context, NetconfMonitoringService.class, null);
+ }
+
+ @Override
+ public NetconfMonitoringService addingService(ServiceReference<NetconfMonitoringService> reference) {
+ Preconditions.checkState(reg == null, "Monitoring service was already added");
+
+ NetconfMonitoringService netconfMonitoringService = super.addingService(reference);
+
+ final NetconfMonitoringOperationService operationService = new NetconfMonitoringOperationService(
+ netconfMonitoringService);
+ NetconfOperationServiceFactory factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+ operationService);
+
+ Dictionary<String, ?> props = new Hashtable<>();
+ reg = context.registerService(NetconfOperationServiceFactory.class, factory, props);
+
+ return netconfMonitoringService;
+ }
+
+ @Override
+ public void removedService(ServiceReference<NetconfMonitoringService> reference,
+ NetconfMonitoringService netconfMonitoringService) {
+ if(reg!=null) {
+ try {
+ reg.unregister();
+ } catch (Exception e) {
+ logger.warn("Ignoring exception while unregistering {}", reg, e);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring.xml;
+
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.transform.dom.DOMResult;
+
+public class JaxBSerializer {
+
+ public Element toXml(NetconfState monitoringModel) {
+ DOMResult res = null;
+ try {
+ JAXBContext jaxbContext = JAXBContext.newInstance(NetconfState.class);
+ Marshaller marshaller = jaxbContext.createMarshaller();
+
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
+
+ res = new DOMResult();
+ marshaller.marshal(monitoringModel, res);
+ } catch (JAXBException e) {
+ throw new RuntimeException("Unable to serialize netconf state " + monitoringModel, e);
+ }
+ return ((Document)res.getNode()).getDocumentElement();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.monitoring.xml.model;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Yang;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+
+import javax.annotation.Nullable;
+import javax.xml.bind.annotation.XmlElement;
+import java.util.Collection;
+
+final class MonitoringSchema {
+
+ private final Schema schema;
+
+ public MonitoringSchema(Schema schema) {
+ this.schema = schema;
+ }
+
+ @XmlElement(name = "identifier")
+ public String getIdentifier() {
+ return schema.getIdentifier();
+ }
+
+ @XmlElement(name = "namespace")
+ public String getNamespace() {
+ return schema.getNamespace().getValue().toString();
+ }
+
+ @XmlElement(name = "location")
+ public Collection<String> getLocation() {
+ return Collections2.transform(schema.getLocation(), new Function<Schema.Location, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable Schema.Location input) {
+ return input.getEnumeration().toString();
+ }
+ });
+ }
+
+ @XmlElement(name = "version")
+ public String getVersion() {
+ return schema.getVersion();
+ }
+
+ @XmlElement(name = "format")
+ public String getFormat() {
+ Preconditions.checkState(schema.getFormat() == Yang.class, "Only yang format permitted, but was %s", schema.getFormat());
+ return "yang";
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring.xml.model;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+final class MonitoringSession {
+
+ @XmlTransient
+ private Session managementSession;
+
+ public MonitoringSession(Session managementSession) {
+ this.managementSession = managementSession;
+ }
+
+ public MonitoringSession() {
+ }
+
+ public void setManagementSession(Session managementSession) {
+ this.managementSession = managementSession;
+ }
+
+ @XmlElement(name = "session-id")
+ public long getId() {
+ return managementSession.getSessionId();
+ }
+
+ @XmlElement(name = "source-host")
+ public String getSourceHost() {
+ return managementSession.getSourceHost().getDomainName().getValue();
+ }
+
+ @XmlElement(name = "login-time")
+ public String getLoginTime() {
+ return managementSession.getLoginTime().getValue();
+ }
+
+ @XmlElement(name = "in-bad-rpcs")
+ public Long getInBadRpcs() {
+ return managementSession.getInBadRpcs().getValue();
+ }
+
+ @XmlElement(name = "in-rpcs")
+ public Long getInRpcs() {
+ return managementSession.getInRpcs().getValue();
+ }
+
+ @XmlElement(name = "out-notifications")
+ public Long getOutNotifications() {
+ return managementSession.getOutNotifications().getValue();
+ }
+
+ @XmlElement(name = "out-rpc-errors")
+ public Long getOutRpcErrors() {
+ return managementSession.getOutRpcErrors().getValue();
+ }
+
+ @XmlElement(name = "transport")
+ public String getTransport() {
+ Preconditions.checkState(managementSession.getTransport() == NetconfSsh.class);
+ return "netconf-ssh";
+ }
+
+ @XmlElement(name = "username")
+ public String getUsername() {
+ return managementSession.getUsername();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.monitoring.xml.model;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+import javax.annotation.Nullable;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Collection;
+
+@XmlRootElement(name = MonitoringConstants.NETCONF_MONITORING_XML_ROOT_ELEMENT)
+public final class NetconfState {
+
+ private Schemas schemas;
+ private Sessions sessions;
+
+ public NetconfState(NetconfMonitoringService monitoringService) {
+ this.sessions = monitoringService.getSessions();
+ this.schemas = monitoringService.getSchemas();
+ }
+
+ public NetconfState() {
+ }
+
+
+
+ @XmlElementWrapper(name="schemas")
+ @XmlElement(name="schema")
+ public Collection<MonitoringSchema> getSchemas() {
+ return Collections2.transform(schemas.getSchema(), new Function<Schema, MonitoringSchema>() {
+ @Nullable
+ @Override
+ public MonitoringSchema apply(@Nullable Schema input) {
+ return new MonitoringSchema(input);
+ }
+ });
+ }
+
+ @XmlElementWrapper(name="sessions")
+ @XmlElement(name="session")
+ public Collection<MonitoringSession> getSessions() {
+ return Collections2.transform(sessions.getSession(), new Function<Session, MonitoringSession>() {
+ @Nullable
+ @Override
+ public MonitoringSession apply(@Nullable Session input) {
+ return new MonitoringSession(input);
+ }
+ });
+ }
+}
--- /dev/null
+@XmlSchema(
+ elementFormDefault = XmlNsForm.QUALIFIED,
+// xmlns = {
+// @XmlNs(namespaceURI = MonitoringConstants.NAMESPACE, prefix = "")
+// }
+ namespace = MonitoringConstants.NAMESPACE
+)
+package org.opendaylight.controller.netconf.monitoring.xml.model;
+
+import org.opendaylight.controller.netconf.monitoring.MonitoringConstants;
+
+import javax.xml.bind.annotation.XmlNsForm;
+import javax.xml.bind.annotation.XmlSchema;
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Public License v1.0 which accompanies this distribution,
+* and is available at http://www.eclipse.org/legal/epl-v10.html
+*/
+package org.opendaylight.controller.netconf.monitoring.xml;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
+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.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SchemasBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.SessionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32;
+import org.w3c.dom.Element;
+
+import java.util.Date;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class JaxBSerializerTest {
+
+ @Test
+ public void testName() throws Exception {
+
+ NetconfMonitoringService service = new NetconfMonitoringService() {
+
+ @Override
+ public Sessions getSessions() {
+ return new SessionsBuilder().setSession(Lists.newArrayList(getMockSession())).build();
+ }
+
+ @Override
+ public Schemas getSchemas() {
+ return new SchemasBuilder().setSchema(Lists.<Schema>newArrayList()).build();
+ }
+ };
+ NetconfState model = new NetconfState(service);
+ Element xml = new JaxBSerializer().toXml(model);
+ System.out.println(XmlUtil.toString(xml));
+ }
+
+ private Session getMockSession() {
+ Session mocked = mock(Session.class);
+ doReturn(1L).when(mocked).getSessionId();
+ doReturn(new DateAndTime(new Date().toString())).when(mocked).getLoginTime();
+ doReturn(new Host(new DomainName("address/port"))).when(mocked).getSourceHost();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInBadRpcs();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInRpcs();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutNotifications();
+ doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutRpcErrors();
+ doReturn(NetconfSsh.class).when(mocked).getTransport();
+ doReturn("username").when(mocked).getUsername();
+ return mocked;
+ }
+}
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.Promise;
-import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfSessionPreferences;
import org.opendaylight.controller.netconf.util.handler.FramingMechanismHandlerFactory;
import org.opendaylight.controller.netconf.util.handler.NetconfMessageAggregator;
channel.pipeline().addAfter("aggregator", "chunkDecoder", new NetconfMessageChunkDecoder());
}
changeState(State.ESTABLISHED);
- S session = getSession(sessionListener, channel, doc);
+ S session = getSession(sessionListener, channel, netconfMessage);
negotiationSuccessful(session);
} else {
final IllegalStateException cause = new IllegalStateException(
"Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+ logger.warn("Negotiation of netconf session failed", cause);
negotiationFailed(cause);
}
}
- protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+ protected abstract S getSession(SessionListener sessionListener, Channel channel, NetconfMessage message);
private boolean isHelloMessage(Document doc) {
try {
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.List;
/**
* NetconfMessageFactory for (de)serializing DOM documents.
@Override
public NetconfMessage parse(byte[] bytes) throws DeserializerException, DocumentedException {
- String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
- logger.debug("Parsing message \n{}", s);
- if (bytes[0] == '[') {
- // yuma sends auth information in the first line. Ignore until ]\n
- // is found.
- int endOfAuthHeader = ByteArray.findByteSequence(bytes, new byte[] { ']', '\n' });
+ logMessage(bytes);
+
+ String additionalHeader = null;
+
+ if (startsWithAdditionalHeader(bytes)) {
+ // Auth information containing username, ip address... extracted for monitoring
+ int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
if (endOfAuthHeader > -1) {
+ byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
+ additionalHeader = additionalHeaderToString(additionalHeaderBytes);
bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
}
}
- NetconfMessage message = null;
+ NetconfMessage message;
try {
Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
- message = new NetconfMessage(doc);
+ message = new NetconfMessage(doc, additionalHeader);
} catch (final SAXException | IOException | IllegalStateException e) {
throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
}
return message;
}
+ private int getAdditionalHeaderEndIndex(byte[] bytes) {
+ for (String possibleEnd : Lists.newArrayList("]\n", "]\r\n")) {
+ int idx = ByteArray.findByteSequence(bytes, possibleEnd.getBytes(Charsets.UTF_8));
+
+ if (idx != -1) {
+ return idx;
+ }
+ }
+
+ return -1;
+ }
+
+ private boolean startsWithAdditionalHeader(byte[] bytes) {
+ List<String> possibleStarts = Lists.newArrayList("[", "\r\n[", "\n[");
+ for (String possibleStart : possibleStarts) {
+ int i = 0;
+ for (byte b : possibleStart.getBytes(Charsets.UTF_8)) {
+ if(bytes[i]!=b)
+ break;
+
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ private void logMessage(byte[] bytes) {
+ String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ logger.debug("Parsing message \n{}", s);
+ }
+
+ private String additionalHeaderToString(byte[] bytes) {
+ return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+ }
+
@Override
public byte[] put(NetconfMessage netconfMessage) {
if (clientId.isPresent()) {
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
+import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring";
// TODO where to store namespace of config ?
public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "urn:opendaylight:params:xml:ns:yang:controller:config";
+ public static final String GET = "get";
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.util.messages;
+
+import com.google.common.io.Files;
+import org.junit.Test;
+
+import java.io.File;
+
+public class NetconfMessageFactoryTest {
+
+
+ @Test
+ public void testAuth() throws Exception {
+ NetconfMessageFactory parser = new NetconfMessageFactory();
+ File authHelloFile = new File(getClass().getResource("/netconfMessages/client_hello_with_auth.xml").getFile());
+ parser.parse(Files.toByteArray(authHelloFile));
+
+ }
+}
[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]
- <?xml version="1.0" encoding="UTF-8"?>
-<hello
- xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<capabilities>
<capability>urn:ietf:params:netconf:base:1.0</capability>
</capabilities>
-</hello>
+</hello>
\ No newline at end of file
<module>netconf-ssh</module>
<module>../../third-party/ganymed</module>
<module>../../third-party/com.siemens.ct.exi</module>
+ <module>netconf-monitoring</module>
+ <module>ietf-netconf-monitoring</module>
</modules>
<profiles>
<artifactId>netconf-impl</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>${netconf.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-persister-api</artifactId>