From: Ed Warnicke Date: Fri, 6 Dec 2013 17:46:06 +0000 (+0000) Subject: Merge "1. Corrected Ip Protocol match field. 2. Added mask for Ipv6 Extension header... X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~240 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=6da86863382c6c850fb2f7c7d2e026d0aa96ae1a;hp=17273a7c91b95c529b2743635e51265e36e0010c Merge "1. Corrected Ip Protocol match field. 2. Added mask for Ipv6 Extension header match field" --- diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 35ebbbe0dd..adc0c0973f 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -265,6 +265,16 @@ config-netconf-connector ${netconf.version} + + org.opendaylight.controller + netconf-monitoring + ${netconf.version} + + + ${project.groupId} + ietf-netconf-monitoring + ${netconf.version} + org.opendaylight.controller config-persister-impl diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java index d28e8e1fd1..6fbbd4d6e2 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/forwardingrulesmanager/consumer/impl/GroupConsumerImpl.java @@ -38,6 +38,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.Rem 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; @@ -53,7 +54,7 @@ import org.slf4j.Logger; 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(); @@ -61,24 +62,12 @@ public class GroupConsumerImpl implements IForwardingRulesManager { private SalGroupService groupService; private GroupDataCommitHandler groupCommitHandler; - private ConcurrentMap originalSwGroupView; - private ConcurrentMap installedSwGroupView; - - private ConcurrentMap> nodeGroups; - private ConcurrentMap inactiveGroups; - - private IClusterContainerServices clusterGroupContainerService = null; private IContainer container; public GroupConsumerImpl() { InstanceIdentifier 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"); @@ -97,130 +86,15 @@ public class GroupConsumerImpl implements IForwardingRulesManager { 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(); - installedSwGroupView = new ConcurrentHashMap(); - nodeGroups = new ConcurrentHashMap>(); - inactiveGroups = new ConcurrentHashMap(); - } - - @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) map; - } else { - logger.error("Retrieval of cache(originalSwGroupView) failed"); - return false; - } - - map = clusterGroupContainerService.getCache("frm.installedSwGroupView"); - if (map != null) { - installedSwGroupView = (ConcurrentMap) map; - } else { - logger.error("Retrieval of cache(installedSwGroupView) failed"); - return false; - } - - map = clusterGroupContainerService.getCache("frm.inactiveGroups"); - if (map != null) { - inactiveGroups = (ConcurrentMap) map; - } else { - logger.error("Retrieval of cache(inactiveGroups) failed"); - return false; - } - - map = clusterGroupContainerService.getCache("frm.nodeGroups"); - if (map != null) { - nodeGroups = (ConcurrentMap>) 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 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); @@ -257,24 +131,28 @@ public class GroupConsumerImpl implements IForwardingRulesManager { * @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; } /** @@ -283,13 +161,13 @@ public class GroupConsumerImpl implements IForwardingRulesManager { * @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(); @@ -299,7 +177,7 @@ public class GroupConsumerImpl implements IForwardingRulesManager { groupData.setGroupType(groupAddDataObject.getGroupType()); groupData.setNode(groupAddDataObject.getNode()); groupService.addGroup(groupData.build()); - return groupOperationStatus; + return; } /** @@ -308,13 +186,13 @@ public class GroupConsumerImpl implements IForwardingRulesManager { * @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(); @@ -324,34 +202,45 @@ public class GroupConsumerImpl implements IForwardingRulesManager { groupData.setGroupType(groupRemoveDataObject.getGroupType()); groupData.setNode(groupRemoveDataObject.getNode()); groupService.removeGroup(groupData.build()); - return groupOperationStatus; + return; } private RpcResult commitToPlugin(InternalTransaction transaction) { - for (Entry, Group> entry : transaction.additions.entrySet()) { - - if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) { - transaction.additions.remove(entry.getKey()); - return Rpcs.getRpcResult(false, null, Collections.emptySet()); - } - } - - for (Entry, Group> entry : transaction.updates.entrySet()) { - - if (!addGroup(entry.getKey(), entry.getValue()).isSuccess()) { - transaction.updates.remove(entry.getKey()); - return Rpcs.getRpcResult(false, null, Collections.emptySet()); - } - } + DataModification, DataObject> modification = transaction.modification; + //get created entries + Set, DataObject>> createdEntries = + modification.getCreatedConfigurationData().entrySet(); + + //get updated entries + Set, DataObject>> updatedEntries = + new HashSet, 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.emptySet()); - } - } + //get removed entries + Set> removeEntriesInstanceIdentifiers = + modification.getRemovedConfigurationData(); + + for (Entry, DataObject> entry : createdEntries) { + if(entry.getValue() instanceof Group) { + addGroup(entry.getKey(), (Group)entry.getValue()); + } + } + + for (Entry, 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.emptySet()); @@ -361,32 +250,21 @@ public class GroupConsumerImpl implements IForwardingRulesManager { @Override public DataCommitTransaction, DataObject> requestCommit( - DataModification, DataObject> modification) { - // We should verify transaction - System.out.println("Coming in GroupDatacommitHandler"); + DataModification, DataObject> modification) { InternalTransaction transaction = new InternalTransaction(modification); transaction.prepareUpdate(); return transaction; } } - private final class InternalTransaction implements DataCommitTransaction, DataObject> { + private final class InternalTransaction implements DataCommitTransaction, DataObject> { private final DataModification, DataObject> modification; - - @Override - public DataModification, DataObject> getModification() { - return modification; - } - - public InternalTransaction(DataModification, DataObject> modification) { + + public InternalTransaction(DataModification, DataObject> modification) { this.modification = modification; } - - Map, Group> additions = new HashMap<>(); - Map, Group> updates = new HashMap<>(); - Set> 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. @@ -394,28 +272,6 @@ public class GroupConsumerImpl implements IForwardingRulesManager { */ void prepareUpdate() { - Set, DataObject>> groupAdded = modification.getCreatedConfigurationData().entrySet(); - for (Entry, DataObject> entry : groupAdded) { - if (entry.getValue() instanceof Group) { - Group group = (Group) entry.getValue(); - additions.put(entry.getKey(), group); - } - - } - - Set, DataObject>> groupUpdate = modification.getUpdatedConfigurationData().entrySet(); - for (Entry, 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(); } /** @@ -425,9 +281,7 @@ public class GroupConsumerImpl implements IForwardingRulesManager { @Override public RpcResult finish() throws IllegalStateException { - RpcResult rpcStatus = commitToPlugin(this); - // We return true if internal transaction is successful. - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + RpcResult rpcStatus = commitToPlugin(this); return rpcStatus; } @@ -437,12 +291,15 @@ public class GroupConsumerImpl implements IForwardingRulesManager { * */ @Override - public RpcResult rollback() throws IllegalStateException { - // NOOP - we did not modified any internal state during - // requestCommit phase - // return Rpcs.getRpcResult(true, null, Collections.emptySet()); + public RpcResult rollback() throws IllegalStateException { + + ///needs to be implemented as per gerrit 3314 return Rpcs.getRpcResult(true, null, Collections.emptySet()); + } + @Override + public DataModification, DataObject> getModification() { + return modification; } } @@ -470,31 +327,4 @@ public class GroupConsumerImpl implements IForwardingRulesManager { } } - - @Override - public List get() { - - List orderedList = new ArrayList(); - Collection groupList = originalSwGroupView.values(); - for (Iterator iterator = groupList.iterator(); iterator.hasNext();) { - orderedList.add(iterator.next()); - } - return orderedList; - } - - @Override - public DataObject getWithName(String name, Node n) { - - if (this instanceof GroupConsumerImpl) { - Collection groupList = originalSwGroupView.values(); - for (Iterator iterator = groupList.iterator(); iterator.hasNext();) { - Group group = iterator.next(); - if (group.getNode().equals(n) && group.getGroupName().equals(name)) { - - return group; - } - } - } - return null; - } -} + } diff --git a/opendaylight/md-sal/sal-binding-it/pom.xml b/opendaylight/md-sal/sal-binding-it/pom.xml index 6a994dbd07..235e6ac51e 100644 --- a/opendaylight/md-sal/sal-binding-it/pom.xml +++ b/opendaylight/md-sal/sal-binding-it/pom.xml @@ -181,6 +181,11 @@ netconf-impl ${netconf.version} + + org.opendaylight.controller + netconf-monitoring + ${netconf.version} + org.opendaylight.controller netconf-client diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 49781ce116..15616215d3 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -53,6 +53,8 @@ public class TestHelper { 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(), // diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlMapper.java index 39525f8599..c0de0d80a8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlMapper.java @@ -22,12 +22,16 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.YangNode; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.google.common.base.Preconditions; public class XmlMapper { + + private final Logger logger = LoggerFactory.getLogger(XmlMapper.class); public Document write(CompositeNode data, DataNodeContainer schema) throws UnsupportedDataTypeException { Preconditions.checkNotNull(data); @@ -70,9 +74,11 @@ public class XmlMapper { } 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)); } @@ -97,14 +103,16 @@ public class XmlMapper { } private DataSchemaNode findFirstSchemaForNode(Node node, Set 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; + } } } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java index 84a9f39520..7ee13aeb58 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java @@ -40,8 +40,6 @@ import java.util.Set; public class Get extends AbstractConfigNetconfOperation { - public static final String GET = "get"; - private final YangStoreSnapshot yangStoreSnapshot; private static final Logger logger = LoggerFactory.getLogger(Get.class); @@ -102,17 +100,17 @@ public class Get extends AbstractConfigNetconfOperation { } 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 @@ -148,7 +146,7 @@ public class Get extends AbstractConfigNetconfOperation { final Element element = runtime.toXml(runtimeBeans, configBeans, document); - logger.info("{} operation successful", GET); + logger.info("{} operation successful", XmlNetconfConstants.GET); return element; } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java index 5055c93583..ed8f02bf56 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java @@ -8,11 +8,8 @@ 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; @@ -25,8 +22,11 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter; 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}. @@ -76,8 +76,6 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { 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> modulesAndContents = yangStoreSnapshot.getModuleMap().values(); for (Map.Entry moduleAndContent : modulesAndContents) { @@ -100,6 +98,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { return capability; } + @Override + public Optional getModuleNamespace() { + return Optional.absent(); + } + @Override public Optional getModuleName() { return Optional.absent(); @@ -114,6 +117,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { public Optional getCapabilitySchema() { return Optional.absent(); } + + @Override + public Optional> getLocation() { + return Optional.absent(); + } } private static class YangStoreCapability extends BasicCapability { @@ -121,12 +129,14 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { private final String content; private final String revision; private final String moduleName; + private final String moduleNamespace; public YangStoreCapability(Map.Entry 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()); } @@ -150,6 +160,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService { return Optional.of(moduleName); } + @Override + public Optional getModuleNamespace() { + return Optional.of(moduleNamespace); + } + @Override public Optional getRevision() { return Optional.of(revision); diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml index 4e34591284..b1f783343d 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml +++ b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml @@ -1,5 +1,5 @@ - - - urn:ietf:params:netconf:base:1.0 - - + + + urn:ietf:params:netconf:base:1.0 + + diff --git a/opendaylight/netconf/ietf-netconf-monitoring/pom.xml b/opendaylight/netconf/ietf-netconf-monitoring/pom.xml new file mode 100644 index 0000000000..f564c8cd87 --- /dev/null +++ b/opendaylight/netconf/ietf-netconf-monitoring/pom.xml @@ -0,0 +1,112 @@ + + + + netconf-subsystem + org.opendaylight.controller + 0.2.3-SNAPSHOT + + 4.0.0 + ietf-netconf-monitoring + ${project.artifactId} + bundle + + + + + org.opendaylight.yangtools.model + ietf-inet-types + + + org.opendaylight.yangtools.model + ietf-yang-types + + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + + + + org.opendaylight.yangtools + yang-maven-plugin + ${yangtools.version} + + + + generate-sources + + + src/main/yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + + + target/generated-sources/monitoring + + + + true + + + + + + org.opendaylight.yangtools + maven-sal-api-gen-plugin + ${yangtools.binding.version} + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + generate-sources + + add-source + + + + target/generated-sources/sal + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + 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, + + + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.* + + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang b/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang new file mode 100644 index 0000000000..391edd07f3 --- /dev/null +++ b/opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang @@ -0,0 +1,593 @@ +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: + WG List: + + WG Chair: Mehmet Ersue + + + WG Chair: Bert Wijnen + + + Editor: Mark Scott + + + Editor: Martin Bjorklund + "; + + 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 messages received."; + } + + leaf in-bad-rpcs { + type yang:zero-based-counter32; + description + "Number of messages received when an message was expected, + that were not correct messages. This includes XML parse + errors and errors on the rpc layer."; + } + + leaf out-rpc-errors { + type yang:zero-based-counter32; + description + "Number of messages sent that contained an + element."; + } + + leaf out-notifications { + type yang:zero-based-counter32; + description + "Number of 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 and 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 + 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 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 + 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 message was received. This includes + 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 message with a 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 + operation, or killed by a + 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 is + 'invalid-value'. + + If more than one schema matches the requested parameters, the + is 'operation-failed', and 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 diff --git a/opendaylight/netconf/netconf-api/pom.xml b/opendaylight/netconf/netconf-api/pom.xml index 0e9b421229..0fce4748a4 100644 --- a/opendaylight/netconf/netconf-api/pom.xml +++ b/opendaylight/netconf/netconf-api/pom.xml @@ -18,6 +18,19 @@ org.opendaylight.controller config-api + + org.opendaylight.yangtools.model + ietf-inet-types + + + org.opendaylight.yangtools.model + ietf-yang-types + + + ${project.groupId} + ietf-netconf-monitoring + + org.opendaylight.bgpcep framework @@ -41,11 +54,17 @@ 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, org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.api.jmx, + org.opendaylight.controller.netconf.api.monitoring, diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java index e5a9e18576..a0fddd79f2 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.netconf.api; +import com.google.common.base.Optional; import org.w3c.dom.Document; /** @@ -20,11 +21,22 @@ public final class NetconfMessage { 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 getAdditionalHeader() { + return additionalHeader== null ? Optional.absent() : Optional.of(additionalHeader); + } } diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java index 17330b7bab..1e14bfdba2 100644 --- a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java @@ -52,6 +52,7 @@ public abstract class NetconfSession extends AbstractProtocolSession getServerCapabilities() { return capabilities; } + } diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java index 17a55c52bc..3340dde883 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java @@ -13,6 +13,7 @@ import com.google.common.collect.Collections2; 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; @@ -66,7 +67,8 @@ public class NetconfClientSessionNegotiator extends } @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())); } } diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml index f1e3f891ee..e073aaca8d 100644 --- a/opendaylight/netconf/netconf-impl/pom.xml +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -19,6 +19,10 @@ ${project.groupId} netconf-api + + ${project.groupId} + ietf-netconf-monitoring + ${project.groupId} netconf-util @@ -32,6 +36,11 @@ netconf-mapping-api + + org.opendaylight.yangtools.model + ietf-inet-types + + org.opendaylight.bgpcep util @@ -112,6 +121,7 @@ io.netty.buffer, io.netty.handler.codec, io.netty.channel.nio, + javax.annotation, javax.management, javax.net.ssl, javax.xml.namespace, @@ -131,7 +141,15 @@ 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, diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java index a4d7e74723..b692179429 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java @@ -8,19 +8,104 @@ 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 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); + } + } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java index 686adcad85..43e55d746a 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java @@ -8,13 +8,13 @@ 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; @@ -25,29 +25,32 @@ import org.slf4j.LoggerFactory; 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 { 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(); } @@ -57,6 +60,7 @@ public class NetconfServerSessionListener implements NetconfTerminationReason netconfTerminationReason) { logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession, netconfTerminationReason.getErrorMessage()); + monitoringService.onSessionDown(netconfNetconfServerSession); operationRouter.close(); } @@ -70,7 +74,7 @@ public class NetconfServerSessionListener implements // 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)) { @@ -78,10 +82,13 @@ public class NetconfServerSessionListener implements } } 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); } } @@ -93,7 +100,7 @@ public class NetconfServerSessionListener implements } private NetconfMessage processDocument(final NetconfMessage netconfMessage, - NetconfSession session) throws NetconfDocumentedException { + NetconfServerSession session) throws NetconfDocumentedException { final Document incommingDocument = netconfMessage.getDocument(); final Node rootNode = incommingDocument.getDocumentElement(); @@ -104,6 +111,9 @@ public class NetconfServerSessionListener implements 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 { diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java index 70ddf299fd..e25eaacdc0 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java @@ -12,6 +12,7 @@ import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; 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 { @@ -22,12 +23,15 @@ public class NetconfServerSessionListenerFactory implements SessionListenerFacto 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 @@ -41,6 +45,6 @@ public class NetconfServerSessionListenerFactory implements SessionListenerFacto netconfOperationServiceSnapshot, capabilityProvider, commitNotifier); - return new NetconfServerSessionListener(operationRouter); + return new NetconfServerSessionListener(operationRouter, monitor); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java index e14ae3e4dc..01ac018b3e 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java @@ -8,26 +8,91 @@ 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 { + static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiator.class); + + private static final AdditionalHeader DEFAULT_HEADER = new AdditionalHeader(); + protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences, Promise 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 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("\\[(?[^;]+);(?
[0-9\\.]+)[:/](?[0-9]+);(?[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(); + } + } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java index 3623ef5032..cc503f60c3 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java @@ -8,9 +8,9 @@ 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; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java index 77042855e6..ba9e4d0f8a 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java @@ -11,9 +11,9 @@ package org.opendaylight.controller.netconf.impl.mapping.operations; 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; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java index 8f08688f59..c18d0cc35f 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java @@ -7,9 +7,9 @@ */ 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.impl.mapping.ExiDecoderHandler; import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler; import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java index 98a7205682..2c6c89617f 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java @@ -7,9 +7,9 @@ */ 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.impl.mapping.ExiDecoderHandler; import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler; import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java index b30c80b43d..abebacf974 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -9,8 +9,7 @@ package org.opendaylight.controller.netconf.impl.osgi; 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; @@ -19,9 +18,15 @@ import org.opendaylight.controller.netconf.impl.SessionIdProvider; 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); @@ -31,6 +36,7 @@ public class NetconfImplActivator implements BundleActivator { private NetconfServerDispatcher dispatch; private NioEventLoopGroup eventLoopGroup; private HashedWheelTimer timer; + private ServiceRegistration regMonitoring; @Override public void start(final BundleContext context) throws Exception { @@ -38,8 +44,7 @@ public class NetconfImplActivator implements BundleActivator { "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(); @@ -48,8 +53,10 @@ public class NetconfImplActivator implements BundleActivator { commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener); + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( - factoriesListener, commitNot, idProvider); + factoriesListener, commitNot, idProvider, monitoringService); eventLoopGroup = new NioEventLoopGroup(); @@ -62,6 +69,19 @@ public class NetconfImplActivator implements BundleActivator { } + 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 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"); @@ -69,5 +89,8 @@ public class NetconfImplActivator implements BundleActivator { commitNot.close(); eventLoopGroup.shutdownGracefully(); timer.stop(); + + regMonitoring.unregister(); + factoriesTracker.close(); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java new file mode 100644 index 0000000000..1b3542595f --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java @@ -0,0 +1,134 @@ +/* + * 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 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 caps = Sets.newHashSet(); + + List 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. emptyList()))); + + builder.setKey(new SchemaKey(Yang.class, identifier, version)); + + schemas.add(builder.build()); + } + + return new SchemasBuilder().setSchema(schemas).build(); + } + + private List transformLocations(List locations) { + List 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 transformSessions(Set sessions) { + return Lists.newArrayList(Collections2.transform(sessions, new Function() { + @Nullable + @Override + public Session apply(@Nullable NetconfManagementSession input) { + return input.toManagementSession(); + } + })); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java index 11b8dc623c..ee474dbba0 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java @@ -10,9 +10,9 @@ package org.opendaylight.controller.netconf.impl.osgi; 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; @@ -53,8 +53,7 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, - CapabilityProvider capabilityProvider, - DefaultCommitNotificationProducer commitNotifier) { + CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) { this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot; @@ -65,12 +64,10 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { .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); @@ -102,7 +99,8 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { final Set 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); } } @@ -116,7 +114,7 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { @Override public synchronized Document onNetconfMessage(Document message, NetconfSession session) throws NetconfDocumentedException { - NetconfOperationExecution netconfOperationExecution = null; + NetconfOperationExecution netconfOperationExecution; String messageAsString = XmlUtil.toString(message); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java index 4d2bcb34fa..c1d9317c29 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java @@ -14,25 +14,25 @@ import org.osgi.util.tracker.ServiceTracker; class NetconfOperationServiceFactoryTracker extends ServiceTracker { - 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 reference) { NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference); - operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory); + factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory); return netconfOperationServiceFactory; } @Override public void removedService(ServiceReference reference, NetconfOperationServiceFactory netconfOperationServiceFactory) { - operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory); + factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory); } } diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java new file mode 100644 index 0000000000..7a0b8b7170 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java @@ -0,0 +1,17 @@ +/* + * 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); +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java index b27cd20172..fca3e8bc1b 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java @@ -8,18 +8,21 @@ 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 { @@ -33,9 +36,8 @@ public final class DeserializerExceptionHandler implements ChannelHandler { @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) { diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java new file mode 100644 index 0000000000..2f8fac23f5 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java @@ -0,0 +1,38 @@ +/* + * 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); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java index c0d2687a8a..ce5233c494 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -14,18 +14,6 @@ import io.netty.channel.ChannelFuture; 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; @@ -43,6 +31,7 @@ import org.opendaylight.controller.netconf.api.NetconfOperationRouter; 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; @@ -54,9 +43,24 @@ import org.opendaylight.controller.netconf.util.xml.XmlUtil; 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; @@ -78,6 +82,8 @@ public class ConcurrentClientsTest { private DefaultCommitNotificationProducer commitNot; private NetconfServerDispatcher dispatch; + @Mock + private SessionMonitoringService monitoring; @Before public void setUp() throws Exception { @@ -103,8 +109,11 @@ public class ConcurrentClientsTest { 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); diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java index eec96592d8..0140b65c14 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java @@ -12,14 +12,15 @@ import io.netty.channel.ChannelFuture; 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; @@ -46,7 +47,7 @@ public class NetconfDispatcherImplTest { new HashedWheelTimer(), factoriesListener, idProvider); NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( - factoriesListener, commitNot, idProvider); + factoriesListener, commitNot, idProvider, null); NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(serverNegotiatorFactory, listenerFactory); diff --git a/opendaylight/netconf/netconf-it/pom.xml b/opendaylight/netconf/netconf-it/pom.xml index 410d9a96aa..f2de91ef46 100644 --- a/opendaylight/netconf/netconf-it/pom.xml +++ b/opendaylight/netconf/netconf-it/pom.xml @@ -76,6 +76,11 @@ netconf-impl test + + ${project.groupId} + netconf-monitoring + test + ${project.groupId} netconf-mapping-api diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java index a2b87c113c..91b543b4de 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java @@ -77,7 +77,7 @@ public class NetconfITSecureTest extends AbstractConfigTest { 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); diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java index 9483785031..ccc7c85eb3 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -17,19 +17,6 @@ import io.netty.channel.ChannelFuture; 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; @@ -57,7 +44,11 @@ import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFa 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; @@ -72,17 +63,35 @@ import org.w3c.dom.Element; 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); @@ -97,7 +106,6 @@ public class NetconfITTest extends AbstractConfigTest { private NetconfClientDispatcher clientDispatcher; - @Before public void setUp() throws Exception { super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray( @@ -125,13 +133,21 @@ public class NetconfITTest extends AbstractConfigTest { 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.emptySet()).when(snap).getServices(); + doReturn(snap).when(factoriesListener).getSnapshot(anyLong()); + return new NetconfMonitoringServiceImpl(factoriesListener); + } + @After public void tearDown() throws Exception { commitNot.close(); @@ -369,23 +385,6 @@ public class NetconfITTest extends AbstractConfigTest { 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); } diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java new file mode 100644 index 0000000000..244e4ba4a9 --- /dev/null +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java @@ -0,0 +1,244 @@ +/* + * 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 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("")) + break; + } + } + + org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("urn:ietf:params:netconf:capability:candidate:1.0")); + org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("tomas")); + } + + 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 caps = Sets.newHashSet(); + caps.add(new Capability() { + @Override + public String getCapabilityUri() { + return "namespaceModuleRevision"; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of("namespace"); + } + + @Override + public Optional getModuleName() { + return Optional.of("name"); + } + + @Override + public Optional getRevision() { + return Optional.of("revision"); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.of("content"); + } + + @Override + public Optional> getLocation() { + return Optional.absent(); + } + }); + + doReturn(caps).when(service).getCapabilities(); + Set services = Sets.newHashSet(service); + doReturn(services).when(snap).getServices(); + doReturn(snap).when(factoriesListener).getSnapshot(anyLong()); + + return factoriesListener; + } + + +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java index 6351c617cf..3dc7155317 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java @@ -10,6 +10,8 @@ package org.opendaylight.controller.netconf.mapping.api; 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. @@ -18,9 +20,13 @@ public interface Capability { public String getCapabilityUri(); + public Optional getModuleNamespace(); + public Optional getModuleName(); public Optional getRevision(); public Optional getCapabilitySchema(); + + public Optional> getLocation(); } diff --git a/opendaylight/netconf/netconf-monitoring/pom.xml b/opendaylight/netconf/netconf-monitoring/pom.xml new file mode 100644 index 0000000000..31e427191c --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/pom.xml @@ -0,0 +1,93 @@ + + + + netconf-subsystem + org.opendaylight.controller + 0.2.3-SNAPSHOT + + 4.0.0 + netconf-monitoring + ${project.artifactId} + bundle + + + + + + ${project.groupId} + netconf-api + + + ${project.groupId} + netconf-util + + + ${project.groupId} + netconf-mapping-api + + + org.opendaylight.yangtools.model + ietf-inet-types + + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + org.opendaylight.bgpcep + mockito-configuration + ${bgpcep.version} + test + + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator + + 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, + + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/Get.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/Get.java new file mode 100644 index 0000000000..0b923f6c5a --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/Get.java @@ -0,0 +1,104 @@ +/* + * 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 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()); + } + +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/MonitoringConstants.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/MonitoringConstants.java new file mode 100644 index 0000000000..3a9d3bacc2 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/MonitoringConstants.java @@ -0,0 +1,19 @@ +/* + * 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"; +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java new file mode 100644 index 0000000000..1143231442 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java @@ -0,0 +1,53 @@ +/* +* 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; + } + } +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java new file mode 100644 index 0000000000..fe01847f59 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java @@ -0,0 +1,104 @@ +/* +* 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 CAPABILITIES = Sets.newHashSet(new Capability() { + + @Override + public String getCapabilityUri() { + return MonitoringConstants.URI; + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(MonitoringConstants.NAMESPACE); + } + + @Override + public Optional getModuleName() { + return Optional.of(MonitoringConstants.MODULE_NAME); + } + + @Override + public Optional getRevision() { + return Optional.of(MonitoringConstants.MODULE_REVISION); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Optional> 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 getCapabilities() { + return CAPABILITIES; + } + + @Override + public Set getNetconfOperations() { + return Collections.emptySet(); + } + + @Override + public Set getFilters() { + return Sets.newHashSet(new Get(monitor)); + } + + @Override + public void close() { + } + +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java new file mode 100644 index 0000000000..920236b9b6 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java @@ -0,0 +1,62 @@ +/* + * 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 { + + private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceTracker.class); + + private ServiceRegistration reg; + + NetconfMonitoringServiceTracker(BundleContext context) { + super(context, NetconfMonitoringService.class, null); + } + + @Override + public NetconfMonitoringService addingService(ServiceReference 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 props = new Hashtable<>(); + reg = context.registerService(NetconfOperationServiceFactory.class, factory, props); + + return netconfMonitoringService; + } + + @Override + public void removedService(ServiceReference reference, + NetconfMonitoringService netconfMonitoringService) { + if(reg!=null) { + try { + reg.unregister(); + } catch (Exception e) { + logger.warn("Ignoring exception while unregistering {}", reg, e); + } + } + } + +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializer.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializer.java new file mode 100644 index 0000000000..4b07ab090a --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializer.java @@ -0,0 +1,36 @@ +/* + * 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(); + } +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSchema.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSchema.java new file mode 100644 index 0000000000..078509db40 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSchema.java @@ -0,0 +1,60 @@ +/* + * 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 getLocation() { + return Collections2.transform(schema.getLocation(), new Function() { + @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"; + } +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java new file mode 100644 index 0000000000..25fb5d44dc --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java @@ -0,0 +1,78 @@ +/* + * 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(); + } +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/NetconfState.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/NetconfState.java new file mode 100644 index 0000000000..98bda5833e --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/NetconfState.java @@ -0,0 +1,64 @@ +/* + * 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 getSchemas() { + return Collections2.transform(schemas.getSchema(), new Function() { + @Nullable + @Override + public MonitoringSchema apply(@Nullable Schema input) { + return new MonitoringSchema(input); + } + }); + } + + @XmlElementWrapper(name="sessions") + @XmlElement(name="session") + public Collection getSessions() { + return Collections2.transform(sessions.getSession(), new Function() { + @Nullable + @Override + public MonitoringSession apply(@Nullable Session input) { + return new MonitoringSession(input); + } + }); + } +} diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/package-info.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/package-info.java new file mode 100644 index 0000000000..8771421a11 --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/package-info.java @@ -0,0 +1,13 @@ +@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 diff --git a/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java new file mode 100644 index 0000000000..cb6e59f83f --- /dev/null +++ b/opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java @@ -0,0 +1,68 @@ +/* +* 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.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; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java index 3ed75a1b16..95d2feb65c 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java @@ -20,8 +20,8 @@ import io.netty.util.concurrent.Future; 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; @@ -130,16 +130,17 @@ public abstract class AbstractNetconfSessionNegotiator

-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 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()) { diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java index cb3606cb4c..a43216917b 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java @@ -10,9 +10,9 @@ package org.opendaylight.controller.netconf.util.messages; 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; diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java index 7fb293f054..c410cf30b0 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java @@ -43,4 +43,5 @@ public class XmlNetconfConstants { 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"; } diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactoryTest.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactoryTest.java new file mode 100644 index 0000000000..f8c90836b9 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactoryTest.java @@ -0,0 +1,25 @@ +/* + * 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)); + + } +} diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml index 174640c4f9..c0aaf8f23d 100644 --- a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml @@ -1,8 +1,6 @@ [tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;] - - + urn:ietf:params:netconf:base:1.0 - + \ No newline at end of file diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index ad8356431e..52b7370e35 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -29,6 +29,8 @@ netconf-ssh ../../third-party/ganymed ../../third-party/com.siemens.ct.exi + netconf-monitoring + ietf-netconf-monitoring @@ -155,6 +157,16 @@ netconf-impl ${netconf.version} + + ${project.groupId} + netconf-monitoring + ${netconf.version} + + + ${project.groupId} + ietf-netconf-monitoring + ${netconf.version} + org.opendaylight.controller config-persister-api