Initial implementation of netconf monitoring module according to http://tools.ietf... 37/3337/12
authorMaros Marsalek <mmarsale@cisco.com>
Fri, 29 Nov 2013 13:23:44 +0000 (14:23 +0100)
committerMaros Marsalek <mmarsale@cisco.com>
Thu, 5 Dec 2013 08:10:24 +0000 (09:10 +0100)
Response from netconf GET operation now contains monitoring information about current netconf state.

Modified netconf-impl module: added monitoring capabilities to netconf-impl module exposed as OSGi service.

Added netconf-monitoring module that provides a filter for netconf GET operation
and adds monitoring information retrieved from netconf-impl to the response of the GET operation.
It implements netconf-mapping-api similar to config-netconf-connector and
currently provides information about current netconf SESSIONS and yang SCHEMAS
(these information is retrieved via the netconf monitoring OSGi service provided by netconf-impl).

Added ietf-netconf-monitoring module that contains sources generated from ietf-netconf-monitoring yang.
These sources serve as an interface between netconf-impl and netconf-monitoring.

Change-Id: I62c457cde42e01781d9039c6dde0a03115134df4
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
57 files changed:
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/sal-binding-it/pom.xml
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java
opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml
opendaylight/netconf/ietf-netconf-monitoring/pom.xml [new file with mode: 0644]
opendaylight/netconf/ietf-netconf-monitoring/src/main/yang/ietf-netconf-monitoring.yang [new file with mode: 0644]
opendaylight/netconf/netconf-api/pom.xml
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfManagementSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java
opendaylight/netconf/netconf-impl/pom.xml
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfMonitoringServiceImpl.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/SessionMonitoringService.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/AdditionalHeaderParserTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java
opendaylight/netconf/netconf-it/pom.xml
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfMonitoringITTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java
opendaylight/netconf/netconf-monitoring/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/Get.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/MonitoringConstants.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringServiceTracker.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializer.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSchema.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/MonitoringSession.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/NetconfState.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/xml/model/package-info.java [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/test/java/org/opendaylight/controller/netconf/monitoring/xml/JaxBSerializerTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java
opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactoryTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml
opendaylight/netconf/pom.xml

index 35ebbbe0dd2455394a06f5ff62e731b57012ac6d..adc0c0973f76309d60b474c5e1dfbddc5fa23e9d 100644 (file)
           <artifactId>config-netconf-connector</artifactId>
           <version>${netconf.version}</version>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-monitoring</artifactId>
+          <version>${netconf.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>${project.groupId}</groupId>
+          <artifactId>ietf-netconf-monitoring</artifactId>
+          <version>${netconf.version}</version>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-persister-impl</artifactId>
index 27d426791f5c65f6fe01aac0a5e51fb2228b1032..c050c034b896f266bb2826ed63d970a45783b949 100644 (file)
             <artifactId>netconf-impl</artifactId>
             <version>${netconf.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>netconf-monitoring</artifactId>
+            <version>${netconf.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>netconf-client</artifactId>
index 49781ce11630a7f589dda76922e1107025b9abba..15616215d35f13950c08167abe7b687490ba1078 100644 (file)
@@ -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(), //
index 84a9f395207d0c6afcf3d3199cbb31abebd61f85..7ee13aeb589c713351f760464f19a4aff48647d3 100644 (file)
@@ -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;
     }
index 5055c935838e974125529d814334d1b616057b66..ed8f02bf569974daeb14b91eeccd81e32d7f0a25 100644 (file)
@@ -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<Map.Entry<Module, String>> modulesAndContents = yangStoreSnapshot.getModuleMap().values();
         for (Map.Entry<Module, String> moduleAndContent : modulesAndContents) {
@@ -100,6 +98,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService {
             return capability;
         }
 
+        @Override
+        public Optional<String> getModuleNamespace() {
+            return Optional.absent();
+        }
+
         @Override
         public Optional<String> getModuleName() {
             return Optional.absent();
@@ -114,6 +117,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService {
         public Optional<String> getCapabilitySchema() {
             return Optional.absent();
         }
+
+        @Override
+        public Optional<List<String>> 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<Module, String> moduleAndContent) {
             super(getAsString(moduleAndContent.getKey()));
             this.content = moduleAndContent.getValue();
             Module module = moduleAndContent.getKey();
             this.moduleName = module.getName();
+            this.moduleNamespace = module.getNamespace().toString();
             this.revision = Util.writeDate(module.getRevision());
         }
 
@@ -150,6 +160,11 @@ public class NetconfOperationServiceImpl implements NetconfOperationService {
             return Optional.of(moduleName);
         }
 
+        @Override
+        public Optional<String> getModuleNamespace() {
+            return Optional.of(moduleNamespace);
+        }
+
         @Override
         public Optional<String> getRevision() {
             return Optional.of(revision);
index 4e34591284e78e23e4b84f0553aea90c53792295..b1f783343d5bfe5a93793e045755c0254c68f537 100644 (file)
@@ -1,5 +1,5 @@
-<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-    <capabilities>
-        <capability>urn:ietf:params:netconf:base:1.0</capability>
-    </capabilities>
-</hello>
+    <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+        <capabilities>
+            <capability>urn:ietf:params:netconf:base:1.0</capability>
+        </capabilities>
+    </hello>
diff --git a/opendaylight/netconf/ietf-netconf-monitoring/pom.xml b/opendaylight/netconf/ietf-netconf-monitoring/pom.xml
new file mode 100644 (file)
index 0000000..f564c8c
--- /dev/null
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>ietf-netconf-monitoring</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-yang-types</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${yangtools.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>src/main/yang</yangFilesRootDir>
+                            <codeGenerators>
+                                <generator>
+                                    <codeGeneratorClass>
+                                        org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+                                    </codeGeneratorClass>
+                                    <outputBaseDir>
+                                        target/generated-sources/monitoring
+                                    </outputBaseDir>
+                                </generator>
+                            </codeGenerators>
+                            <inspectDependencies>true</inspectDependencies>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>maven-sal-api-gen-plugin</artifactId>
+                        <version>${yangtools.binding.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.7</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>target/generated-sources/sal</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            com.google.common.collect,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+                            org.opendaylight.yangtools.yang.binding,
+                            org.opendaylight.yangtools.yang.common,
+                        </Import-Package>
+                        <Export-Package>
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.*
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
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 (file)
index 0000000..391edd0
--- /dev/null
@@ -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:   <http://tools.ietf.org/wg/netconf/>
+     WG List:  <mailto:netconf@ietf.org>
+
+     WG Chair: Mehmet Ersue
+               <mailto:mehmet.ersue@nsn.com>
+
+     WG Chair: Bert Wijnen
+               <mailto:bertietf@bwijnen.net>
+
+     Editor:   Mark Scott
+               <mailto:mark.scott@ericsson.com>
+
+     Editor:   Martin Bjorklund
+               <mailto:mbj@tail-f.com>";
+
+    description
+      "NETCONF Monitoring Module.
+     All elements in this module are read-only.
+
+     Copyright (c) 2010 IETF Trust and the persons identified as
+     authors of the code. All rights reserved.
+
+     Redistribution and use in source and binary forms, with or
+     without modification, is permitted pursuant to, and subject
+     to the license terms contained in, the Simplified BSD
+     License set forth in Section 4.c of the IETF Trust's
+     Legal Provisions Relating to IETF Documents
+     (http://trustee.ietf.org/license-info).
+
+     This version of this YANG module is part of RFC 6022; see
+     the RFC itself for full legal notices.";
+
+    revision "2010-10-04" {
+      description "Initial revision.";
+      reference
+        "RFC 6022: YANG Module for NETCONF Monitoring";
+
+    }
+
+
+    typedef netconf-datastore-type {
+      type enumeration {
+        enum "running" {
+          value 0;
+        }
+        enum "candidate" {
+          value 1;
+        }
+        enum "startup" {
+          value 2;
+        }
+      }
+      description
+        "Enumeration of possible NETCONF datastore types.";
+      reference
+        "RFC 4741: NETCONF Configuration Protocol";
+
+    }
+
+    identity transport {
+      description
+        "Base identity for NETCONF transport types.";
+    }
+
+    identity netconf-ssh {
+      base transport;
+      description
+        "NETCONF over Secure Shell (SSH).";
+      reference
+        "RFC 4742: Using the NETCONF Configuration Protocol
+                 over Secure SHell (SSH)";
+
+    }
+
+    identity netconf-soap-over-beep {
+      base transport;
+      description
+        "NETCONF over Simple Object Access Protocol (SOAP) over
+       Blocks Extensible Exchange Protocol (BEEP).";
+      reference
+        "RFC 4743: Using NETCONF over the Simple Object
+                 Access Protocol (SOAP)";
+
+    }
+
+    identity netconf-soap-over-https {
+      base transport;
+      description
+        "NETCONF over Simple Object Access Protocol (SOAP)
+      over Hypertext Transfer Protocol Secure (HTTPS).";
+      reference
+        "RFC 4743: Using NETCONF over the Simple Object
+                 Access Protocol (SOAP)";
+
+    }
+
+    identity netconf-beep {
+      base transport;
+      description
+        "NETCONF over Blocks Extensible Exchange Protocol (BEEP).";
+      reference
+        "RFC 4744: Using the NETCONF Protocol over the
+                 Blocks Extensible Exchange Protocol (BEEP)";
+
+    }
+
+    identity netconf-tls {
+      base transport;
+      description
+        "NETCONF over Transport Layer Security (TLS).";
+      reference
+        "RFC 5539: NETCONF over Transport Layer Security (TLS)";
+
+    }
+
+    identity schema-format {
+      description
+        "Base identity for data model schema languages.";
+    }
+
+    identity xsd {
+      base schema-format;
+      description
+        "W3C XML Schema Definition.";
+      reference
+        "W3C REC REC-xmlschema-1-20041028:
+          XML Schema Part 1: Structures";
+
+    }
+
+    identity yang {
+      base schema-format;
+      description
+        "The YANG data modeling language for NETCONF.";
+      reference
+        "RFC 6020:  YANG - A Data Modeling Language for the
+                  Network Configuration Protocol (NETCONF)";
+
+    }
+
+    identity yin {
+      base schema-format;
+      description "The YIN syntax for YANG.";
+      reference
+        "RFC 6020:  YANG - A Data Modeling Language for the
+                  Network Configuration Protocol (NETCONF)";
+
+    }
+
+    identity rng {
+      base schema-format;
+      description
+        "Regular Language for XML Next Generation (RELAX NG).";
+      reference
+        "ISO/IEC 19757-2:2008: RELAX NG";
+
+    }
+
+    identity rnc {
+      base schema-format;
+      description "Relax NG Compact Syntax";
+      reference
+        "ISO/IEC 19757-2:2008: RELAX NG";
+
+    }
+
+    grouping common-counters {
+      description
+        "Counters that exist both per session, and also globally,
+       accumulated from all sessions.";
+      leaf in-rpcs {
+        type yang:zero-based-counter32;
+        description
+          "Number of correct <rpc> messages received.";
+      }
+
+      leaf in-bad-rpcs {
+        type yang:zero-based-counter32;
+        description
+          "Number of messages received when an <rpc> message was expected,
+         that were not correct <rpc> messages.  This includes XML parse
+         errors and errors on the rpc layer.";
+      }
+
+      leaf out-rpc-errors {
+        type yang:zero-based-counter32;
+        description
+          "Number of <rpc-reply> messages sent that contained an
+         <rpc-error> element.";
+      }
+
+      leaf out-notifications {
+        type yang:zero-based-counter32;
+        description
+          "Number of <notification> messages sent.";
+      }
+    }  // grouping common-counters
+
+    container netconf-state {
+      config false;
+      description
+        "The netconf-state container is the root of the monitoring
+       data model.";
+      container capabilities {
+        description
+          "Contains the list of NETCONF capabilities supported by the
+         server.";
+        leaf-list capability {
+          type inet:uri;
+          description
+            "List of NETCONF capabilities supported by the server.";
+        }
+      }  // container capabilities
+
+      container datastores {
+        description
+          "Contains the list of NETCONF configuration datastores.";
+        list datastore {
+          key "name";
+          description
+            "List of NETCONF configuration datastores supported by
+           the NETCONF server and related information.";
+          leaf name {
+            type netconf-datastore-type;
+            description
+              "Name of the datastore associated with this list entry.";
+          }
+
+          container locks {
+            presence
+              "This container is present only if the datastore
+             is locked.";
+            description
+              "The NETCONF <lock> and <partial-lock> operations allow
+             a client to lock specific resources in a datastore.  The
+             NETCONF server will prevent changes to the locked
+             resources by all sessions except the one that acquired
+             the lock(s).
+
+             Monitoring information is provided for each datastore
+             entry including details such as the session that acquired
+             the lock, the type of lock (global or partial) and the
+             list of locked resources.  Multiple locks per datastore
+             are supported.";
+            grouping lock-info {
+              description
+                "Lock related parameters, common to both global and
+               partial locks.";
+              leaf locked-by-session {
+                type uint32;
+                mandatory true;
+                description
+                  "The session ID of the session that has locked
+                 this resource.  Both a global lock and a partial
+                 lock MUST contain the NETCONF session-id.
+
+                 If the lock is held by a session that is not managed
+                 by the NETCONF server (e.g., a CLI session), a session
+                 id of 0 (zero) is reported.";
+                reference
+                  "RFC 4741: NETCONF Configuration Protocol";
+
+              }
+
+              leaf locked-time {
+                type yang:date-and-time;
+                mandatory true;
+                description
+                  "The date and time of when the resource was
+                 locked.";
+              }
+            }  // grouping lock-info
+            choice lock-type {
+              description
+                "Indicates if a global lock or a set of partial locks
+               are set.";
+              container global-lock {
+                description
+                  "Present if the global lock is set.";
+                uses lock-info;
+              }  // container global-lock
+              list partial-lock {
+                key "lock-id";
+                description
+                  "List of partial locks.";
+                reference
+                  "RFC 5717: Partial Lock Remote Procedure Call (RPC) for
+                         NETCONF";
+
+                leaf lock-id {
+                  type uint32;
+                  description
+                    "This is the lock id returned in the <partial-lock>
+                   response.";
+                }
+
+                uses lock-info;
+
+                leaf-list select {
+                  type yang:xpath1.0;
+                  min-elements 1;
+                  description
+                    "The xpath expression that was used to request
+                   the lock.  The select expression indicates the
+                   original intended scope of the lock.";
+                }
+
+                leaf-list locked-node {
+                  type instance-identifier;
+                  description
+                    "The list of instance-identifiers (i.e., the
+                   locked nodes).
+
+                   The scope of the partial lock is defined by the list
+                   of locked nodes.";
+                }
+              }  // list partial-lock
+            }  // choice lock-type
+          }  // container locks
+        }  // list datastore
+      }  // container datastores
+
+      container schemas {
+        description
+          "Contains the list of data model schemas supported by the
+         server.";
+        list schema {
+          key "identifier version format";
+          description
+            "List of data model schemas supported by the server.";
+          leaf identifier {
+            type string;
+            description
+              "Identifier to uniquely reference the schema.  The
+             identifier is used in the <get-schema> operation and may
+             be used for other purposes such as file retrieval.
+
+             For modeling languages that support or require a data
+             model name (e.g., YANG module name) the identifier MUST
+             match that name.  For YANG data models, the identifier is
+             the name of the module or submodule.  In other cases, an
+             identifier such as a filename MAY be used instead.";
+          }
+
+          leaf version {
+            type string;
+            description
+              "Version of the schema supported.  Multiple versions MAY be
+             supported simultaneously by a NETCONF server.  Each
+             version MUST be reported individually in the schema list,
+             i.e., with same identifier, possibly different location,
+             but different version.
+
+             For YANG data models, version is the value of the most
+             recent YANG 'revision' statement in the module or
+             submodule, or the empty string if no 'revision' statement
+             is present.";
+          }
+
+          leaf format {
+            type identityref {
+              base schema-format;
+            }
+            description
+              "The data modeling language the schema is written
+             in (currently xsd, yang, yin, rng, or rnc).
+             For YANG data models, 'yang' format MUST be supported and
+             'yin' format MAY also be provided.";
+          }
+
+          leaf namespace {
+            type inet:uri;
+            mandatory true;
+            description
+              "The XML namespace defined by the data model.
+
+             For YANG data models, this is the module's namespace.
+             If the list entry describes a submodule, this field
+             contains the namespace of the module to which the
+             submodule belongs.";
+          }
+
+          leaf-list location {
+            type union {
+              type enumeration {
+                enum "NETCONF" {
+                  value 0;
+                }
+              }
+              type inet:uri;
+            }
+            description
+              "One or more locations from which the schema can be
+             retrieved.  This list SHOULD contain at least one
+             entry per schema.
+
+             A schema entry may be located on a remote file system
+             (e.g., reference to file system for ftp retrieval) or
+             retrieved directly from a server supporting the
+             <get-schema> operation (denoted by the value 'NETCONF').";
+          }
+        }  // list schema
+      }  // container schemas
+
+      container sessions {
+        description
+          "The sessions container includes session-specific data for
+         NETCONF management sessions.  The session list MUST include
+         all currently active NETCONF sessions.";
+        list session {
+          key "session-id";
+          description
+            "All NETCONF sessions managed by the NETCONF server
+           MUST be reported in this list.";
+          leaf session-id {
+            type uint32 {
+              range "1..max";
+            }
+            description
+              "Unique identifier for the session.  This value is the
+             NETCONF session identifier, as defined in RFC 4741.";
+            reference
+              "RFC 4741: NETCONF Configuration Protocol";
+
+          }
+
+          leaf transport {
+            type identityref {
+              base transport;
+            }
+            mandatory true;
+            description
+              "Identifies the transport for each session, e.g.,
+            'netconf-ssh', 'netconf-soap', etc.";
+          }
+
+          leaf username {
+            type string;
+            mandatory true;
+            description
+              "The username is the client identity that was authenticated
+            by the NETCONF transport protocol.  The algorithm used to
+            derive the username is NETCONF transport protocol specific
+            and in addition specific to the authentication mechanism
+            used by the NETCONF transport protocol.";
+          }
+
+          leaf source-host {
+            type inet:host;
+            description
+              "Host identifier of the NETCONF client.  The value
+             returned is implementation specific (e.g., hostname,
+             IPv4 address, IPv6 address)";
+          }
+
+          leaf login-time {
+            type yang:date-and-time;
+            mandatory true;
+            description
+              "Time at the server at which the session was established.";
+          }
+
+          uses common-counters {
+            description
+              "Per-session counters.  Zero based with following reset
+             behaviour:
+               - at start of a session
+               - when max value is reached";
+          }
+        }  // list session
+      }  // container sessions
+
+      container statistics {
+        description
+          "Statistical data pertaining to the NETCONF server.";
+        leaf netconf-start-time {
+          type yang:date-and-time;
+          description
+            "Date and time at which the management subsystem was
+           started.";
+        }
+
+        leaf in-bad-hellos {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions silently dropped because an
+          invalid <hello> message was received.  This includes <hello>
+          messages with a 'session-id' attribute, bad namespace, and
+          bad capability declarations.";
+        }
+
+        leaf in-sessions {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions started.  This counter is incremented
+           when a <hello> message with a <session-id> is sent.
+
+          'in-sessions' - 'in-bad-hellos' =
+              'number of correctly started netconf sessions'";
+        }
+
+        leaf dropped-sessions {
+          type yang:zero-based-counter32;
+          description
+            "Number of sessions that were abnormally terminated, e.g.,
+           due to idle timeout or transport close.  This counter is not
+           incremented when a session is properly closed by a
+           <close-session> operation, or killed by a <kill-session>
+           operation.";
+        }
+
+        uses common-counters {
+          description
+            "Global counters, accumulated from all sessions.
+           Zero based with following reset behaviour:
+             - re-initialization of NETCONF server
+             - when max value is reached";
+        }
+      }  // container statistics
+    }  // container netconf-state
+
+    rpc get-schema {
+      description
+        "This operation is used to retrieve a schema from the
+       NETCONF server.
+
+       Positive Response:
+         The NETCONF server returns the requested schema.
+
+       Negative Response:
+         If requested schema does not exist, the <error-tag> is
+         'invalid-value'.
+
+         If more than one schema matches the requested parameters, the
+         <error-tag> is 'operation-failed', and <error-app-tag> is
+         'data-not-unique'.";
+      input {
+        leaf identifier {
+          type string;
+          mandatory true;
+          description
+            "Identifier for the schema list entry.";
+        }
+
+        leaf version {
+          type string;
+          description
+            "Version of the schema requested.  If this parameter is not
+           present, and more than one version of the schema exists on
+           the server, a 'data-not-unique' error is returned, as
+           described above.";
+        }
+
+        leaf format {
+          type identityref {
+            base schema-format;
+          }
+          description
+            "The data modeling language of the schema.  If this
+            parameter is not present, and more than one formats of
+            the schema exists on the server, a 'data-not-unique' error
+            is returned, as described above.";
+        }
+      }
+
+      output {
+        anyxml data {
+          description
+            "Contains the schema content.";
+        }
+      }
+    }  // rpc get-schema
+} // module
\ No newline at end of file
index 0e9b421229873365a9fe08785f41f1b93ff6834e..0fce4748a40af53113d865492dd1f6ada93ce19a 100644 (file)
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>config-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-yang-types</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ietf-netconf-monitoring</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.opendaylight.bgpcep</groupId>
             <artifactId>framework</artifactId>
                             io.netty.channel,
                             io.netty.util.concurrent,
                             org.w3c.dom,
-                            org.slf4j
+                            org.slf4j,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+                            com.google.common.base,
                         </Import-Package>
                         <Export-Package>
                             org.opendaylight.controller.netconf.api,
                             org.opendaylight.controller.netconf.api.jmx,
+                            org.opendaylight.controller.netconf.api.monitoring,
                         </Export-Package>
                     </instructions>
                 </configuration>
index e5a9e18576afbca754a3b111b4574ae54f9ff64f..a0fddd79f2f0fcff8afced3ab91fcf21aeeea44d 100644 (file)
@@ -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<String> getAdditionalHeader() {
+        return additionalHeader== null ? Optional.<String>absent() : Optional.of(additionalHeader);
+    }
 }
index 17330b7babde5ce12030568ffca5d12207edb946..1e14bfdba285ec0e8f68f3b0b363abe2e370c66b 100644 (file)
@@ -52,6 +52,7 @@ public abstract class NetconfSession extends AbstractProtocolSession<NetconfMess
     @Override
     public void close() {
         channel.close();
+        up = false;
         sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed"));
     }
 
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfManagementSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfManagementSession.java
new file mode 100644 (file)
index 0000000..3c7db49
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.api.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+
+public interface NetconfManagementSession {
+
+    Session toManagementSession();
+}
diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/monitoring/NetconfMonitoringService.java
new file mode 100644 (file)
index 0000000..51eea93
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.netconf.api.monitoring;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Sessions;
+
+public interface NetconfMonitoringService {
+
+    Sessions getSessions();
+
+    Schemas getSchemas();
+}
index c2c8d38b9a8daa08804a686e75106f4a86a60a58..c57487f70b8d12779d88b68c7e20fe879739790a 100644 (file)
@@ -31,4 +31,5 @@ public class NetconfClientSession extends NetconfSession {
     public Collection<String> getServerCapabilities() {
         return capabilities;
     }
+
 }
index 17a55c52bcbf7b1a6dd13a9afcbaa61a382d9388..3340dde883f09cfb077c6c1dcc6b6ea1b0ca8592 100644 (file)
@@ -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()));
     }
 }
index f1e3f891ee3ff70bda9cfd9fefcc73c714a824e3..e073aaca8d039dddf4f6c976dcbe1e30de1b4dd7 100644 (file)
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ietf-netconf-monitoring</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-util</artifactId>
             <artifactId>netconf-mapping-api</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.opendaylight.bgpcep</groupId>
             <artifactId>util</artifactId>
                             io.netty.buffer,
                             io.netty.handler.codec,
                             io.netty.channel.nio,
+                            javax.annotation,
                             javax.management,
                             javax.net.ssl,
                             javax.xml.namespace,
                             org.w3c.dom,
                             org.xml.sax,
                             org.opendaylight.controller.netconf.util.messages,
-                            com.siemens.ct.exi.exceptions
+                            com.siemens.ct.exi.exceptions,
+                            io.netty.util.internal,
+                            org.opendaylight.controller.netconf.api.monitoring,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
                         </Import-Package>
                     </instructions>
                 </configuration>
index a4d7e747234ce07804a1405ff4f96ff31ecd35e5..b6921794296e3f04d02d6e36ef45762a8888ecec 100644 (file)
 
 package org.opendaylight.controller.netconf.impl;
 
+import com.google.common.base.Preconditions;
 import io.netty.channel.Channel;
-
 import org.opendaylight.controller.netconf.api.NetconfSession;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.protocol.framework.SessionListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.DomainName;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfSsh;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.Transport;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.Session;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions.SessionKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.ZeroBasedCounter32;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class NetconfServerSession extends NetconfSession {
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NetconfServerSession extends NetconfSession implements NetconfManagementSession {
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class);
 
-    public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) {
-        super(sessionListener,channel,sessionId);
+    private final NetconfServerSessionNegotiator.AdditionalHeader header;
+
+    private Date loginTime;
+    private long inRpcSuccess, inRpcFail, outRpcError;
+
+    public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId,
+            NetconfServerSessionNegotiator.AdditionalHeader header) {
+        super(sessionListener, channel, sessionId);
+        this.header = header;
         logger.debug("Session {} created", toString());
     }
+
+    @Override
+    protected void sessionUp() {
+        super.sessionUp();
+        Preconditions.checkState(loginTime == null, "Session is already up");
+        this.loginTime = new Date();
+    }
+
+    public void onIncommingRpcSuccess() {
+        inRpcSuccess++;
+    }
+
+    public void onIncommingRpcFail() {
+        inRpcFail++;
+    }
+
+    public void onOutgoingRpcError() {
+        outRpcError++;
+    }
+
+    public static final String ISO_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+
+    @Override
+    public Session toManagementSession() {
+        SessionBuilder builder = new SessionBuilder();
+
+        builder.setSessionId(getSessionId());
+        builder.setSourceHost(new Host(new DomainName(header.getAddress())));
+
+        Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
+        String formattedDateTime = formatDateTime(loginTime);
+        String pattern = DateAndTime.PATTERN_CONSTANTS.get(0);
+        Matcher matcher = Pattern.compile(pattern).matcher(formattedDateTime);
+        Preconditions.checkState(matcher.matches(), "Formatted datetime %s does not match pattern %s", formattedDateTime, pattern);
+        builder.setLoginTime(new DateAndTime(formattedDateTime));
+
+        builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
+        builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
+        builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));
+
+        builder.setUsername(header.getUsername());
+        builder.setTransport(getTransportForString(header.getTransport()));
+
+        builder.setOutNotifications(new ZeroBasedCounter32(0L));
+
+        builder.setKey(new SessionKey(getSessionId()));
+        return builder.build();
+    }
+
+    private Class<? extends Transport> getTransportForString(String transport) {
+        switch(transport) {
+            case "ssh" : return NetconfSsh.class;
+            // TODO what about tcp
+            case "tcp" : return NetconfSsh.class;
+            default: throw new IllegalArgumentException("Unknown transport type " + transport);
+        }
+    }
+
+    private String formatDateTime(Date loginTime) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat(ISO_DATE_FORMAT);
+        return dateFormat.format(loginTime);
+    }
+
 }
index 686adcad8550a3401809235cc0bda6e660782738..43e55d746a4ee59d1ec9bb35245f74e0edbca018 100644 (file)
@@ -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<NetconfMessage, NetconfServerSession, NetconfTerminationReason> {
 
     static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
     public static final String MESSAGE_ID = "message-id";
+    private final SessionMonitoringService monitoringService;
 
     private NetconfOperationRouterImpl operationRouter;
 
-    public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+    public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter,
+                                        SessionMonitoringService monitoringService) {
         this.operationRouter = operationRouter;
+        this.monitoringService = monitoringService;
     }
 
     @Override
     public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
-
+        monitoringService.onSessionUp(netconfNetconfServerSession);
     }
 
     @Override
     public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
         logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+        monitoringService.onSessionDown(netconfNetconfServerSession);
 
         operationRouter.close();
     }
@@ -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 {
index 70ddf299fd5dd0d7ff8c091598c0644a88278342..e25eaacdc06c9bbc0f9b600194bd9171787d1530 100644 (file)
@@ -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<NetconfServerSessionListener> {
@@ -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);
     }
 }
index e14ae3e4dca088cbd7d016ac119e6003bb8525a9..01ac018b3eadc3ee85d6ffd7a5e12fb8cbaf2daa 100644 (file)
@@ -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<NetconfServerSessionPreferences, NetconfServerSession> {
 
+    static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionNegotiator.class);
+
+    private static final AdditionalHeader DEFAULT_HEADER = new AdditionalHeader();
+
     protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences,
             Promise<NetconfServerSession> promise, Channel channel, Timer timer, SessionListener sessionListener) {
         super(sessionPreferences, promise, channel, timer, sessionListener);
     }
 
     @Override
-    protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, Document doc) {
-        return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId());
+    protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, NetconfMessage message) {
+        Optional<String> additionalHeader = message.getAdditionalHeader();
+
+        AdditionalHeader parsedHeader;
+        if (additionalHeader.isPresent()) {
+            parsedHeader = new AdditionalHeader(additionalHeader.get());
+        } else {
+            parsedHeader = DEFAULT_HEADER;
+        }
+        logger.debug("Additional header from hello parsed as {} from {}", parsedHeader, additionalHeader);
+
+        return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId(), parsedHeader);
     }
 
+    static class AdditionalHeader {
+
+        private static final Pattern pattern = Pattern
+                .compile("\\[(?<username>[^;]+);(?<address>[0-9\\.]+)[:/](?<port>[0-9]+);(?<transport>[a-z]+)[^\\]]+\\]");
+        private final String username;
+        private final String address;
+        private final String transport;
+
+        public AdditionalHeader(String addHeaderAsString) {
+            addHeaderAsString = addHeaderAsString.trim();
+            Matcher matcher = pattern.matcher(addHeaderAsString);
+            Preconditions.checkArgument(matcher.matches(), "Additional header in wrong format %s, expected %s",
+                    addHeaderAsString, pattern);
+            this.username = matcher.group("username");
+            this.address = matcher.group("address");
+            this.transport = matcher.group("transport");
+        }
+
+        private AdditionalHeader() {
+            this.username = this.address = "unknown";
+            this.transport = "ssh";
+        }
+
+        String getUsername() {
+            return username;
+        }
+
+        String getAddress() {
+            return address;
+        }
+
+        String getTransport() {
+            return transport;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuffer sb = new StringBuffer("AdditionalHeader{");
+            sb.append("username='").append(username).append('\'');
+            sb.append(", address='").append(address).append('\'');
+            sb.append(", transport='").append(transport).append('\'');
+            sb.append('}');
+            return sb.toString();
+        }
+    }
 }
index 3623ef50325378b1c710d005248be520479c6763..cc503f60c3938947b8acb29882a9cd161bafda5c 100644 (file)
@@ -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;
index 77042855e6525673a69fcce76344d5ea757b4061..ba9e4d0f8a14fe9cd44c906dc57a91011a89ca99 100644 (file)
@@ -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;
index 8f08688f59dbcf456e7b4580bfbd8f1ea661235c..c18d0cc35f958bc543106810c91d3e11ced1d920 100644 (file)
@@ -7,9 +7,9 @@
  */\r
 package org.opendaylight.controller.netconf.impl.mapping.operations;\r
 \r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
 import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
 import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
 import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
index 98a72056827de18dbf034540ea56636a6ec3b922..2c6c89617ffbe19b1ff383604ba0e924fdb75ba6 100644 (file)
@@ -7,9 +7,9 @@
  */\r
 package org.opendaylight.controller.netconf.impl.mapping.operations;\r
 \r
+import org.opendaylight.controller.netconf.api.NetconfSession;\r
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;\r
-import org.opendaylight.controller.netconf.api.NetconfSession;\r
 import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;\r
 import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;\r
 import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;\r
index b30c80b43d964426839306477a3c84a4d28084fd..abebacf974d0527b72d6479fb2e83796e18aedd4 100644 (file)
@@ -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<NetconfMonitoringService> 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<String, ?> dic = new Hashtable<>();
+        regMonitoring = context.registerService(NetconfMonitoringService.class, netconfMonitoringServiceImpl, dic);
+
+        return netconfMonitoringServiceImpl;
+    }
+
     @Override
     public void stop(final BundleContext context) throws Exception {
         logger.info("Shutting down netconf because YangStoreService service was removed");
@@ -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 (file)
index 0000000..1b35425
--- /dev/null
@@ -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<NetconfManagementSession> sessions = new ConcurrentSet<>();
+    private final NetconfOperationServiceFactoryListener factoriesListener;
+
+    public NetconfMonitoringServiceImpl(NetconfOperationServiceFactoryListener factoriesListener) {
+        this.factoriesListener = factoriesListener;
+    }
+
+    @Override
+    public void onSessionUp(NetconfManagementSession session) {
+        logger.debug("Session {} up", session);
+        Preconditions.checkState(sessions.contains(session) == false, "Session %s was already added", session);
+        sessions.add(session);
+    }
+
+    @Override
+    public void onSessionDown(NetconfManagementSession session) {
+        logger.debug("Session {} down", session);
+        Preconditions.checkState(sessions.contains(session) == true, "Session %s not present", session);
+        sessions.remove(session);
+    }
+
+    @Override
+    public Sessions getSessions() {
+        return new SessionsBuilder().setSession(transformSessions(sessions)).build();
+    }
+
+    @Override
+    public Schemas getSchemas() {
+        // FIXME, session ID
+        return transformSchemas(factoriesListener.getSnapshot(0));
+    }
+
+    private Schemas transformSchemas(NetconfOperationServiceSnapshot snapshot) {
+        Set<Capability> caps = Sets.newHashSet();
+
+        List<Schema> schemas = Lists.newArrayList();
+
+        for (NetconfOperationService netconfOperationService : snapshot.getServices()) {
+            // TODO check for duplicates ? move capability merging to snapshot
+            caps.addAll(netconfOperationService.getCapabilities());
+        }
+
+        for (Capability cap : caps) {
+            SchemaBuilder builder = new SchemaBuilder();
+
+            if(cap.getCapabilitySchema().isPresent() == false)
+                continue;
+
+            Preconditions.checkState(cap.getModuleNamespace().isPresent());
+            builder.setNamespace(new Uri(cap.getModuleNamespace().get()));
+
+            Preconditions.checkState(cap.getRevision().isPresent());
+            String version = cap.getRevision().get();
+            builder.setVersion(version);
+
+            Preconditions.checkState(cap.getModuleName().isPresent());
+            String identifier = cap.getModuleName().get();
+            builder.setIdentifier(identifier);
+
+            builder.setFormat(Yang.class);
+
+            builder.setLocation(transformLocations(cap.getLocation().or(Collections.<String> emptyList())));
+
+            builder.setKey(new SchemaKey(Yang.class, identifier, version));
+
+            schemas.add(builder.build());
+        }
+
+        return new SchemasBuilder().setSchema(schemas).build();
+    }
+
+    private List<Schema.Location> transformLocations(List<String> locations) {
+        List<Schema.Location> monitoringLocations = Lists.newArrayList();
+        monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.NETCONF));
+
+        for (String location : locations) {
+            // TODO how to create enumerration from string location ?
+            // monitoringLocations.add(new Schema.Location(Schema.Location.Enumeration.valueOf(location)));
+        }
+
+        return monitoringLocations;
+    }
+
+    private List<Session> transformSessions(Set<NetconfManagementSession> sessions) {
+        return Lists.newArrayList(Collections2.transform(sessions, new Function<NetconfManagementSession, Session>() {
+            @Nullable
+            @Override
+            public Session apply(@Nullable NetconfManagementSession input) {
+                return input.toManagementSession();
+            }
+        }));
+    }
+}
index 11b8dc623c8a07903df1d05da46407aef1770a47..ee474dbba01f4068de6190662c50d25f8339749d 100644 (file)
@@ -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<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
             for (NetconfOperationFilter filter : filtersFromService) {
-                Preconditions.checkState(result.contains(filter) == false, "Filter %s already present", filter);
+                Preconditions.checkState(result.contains(filter) == false,
+                        "Filter %s already present, all filters so far: %s", filter, result);
                 result.add(filter);
             }
         }
@@ -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);
 
index 4d2bcb34fa687bcd0886c509d7678cae633be6dd..c1d9317c294ca96938dbd0ccec843eb7d5991d0f 100644 (file)
@@ -14,25 +14,25 @@ import org.osgi.util.tracker.ServiceTracker;
 
 class NetconfOperationServiceFactoryTracker extends
         ServiceTracker<NetconfOperationServiceFactory, NetconfOperationServiceFactory> {
-    private final NetconfOperationServiceFactoryListener operationRouter;
+    private final NetconfOperationServiceFactoryListener factoriesListener;
 
     NetconfOperationServiceFactoryTracker(BundleContext context,
-            final NetconfOperationServiceFactoryListener operationRouter) {
+            final NetconfOperationServiceFactoryListener factoriesListener) {
         super(context, NetconfOperationServiceFactory.class, null);
-        this.operationRouter = operationRouter;
+        this.factoriesListener = factoriesListener;
     }
 
     @Override
     public NetconfOperationServiceFactory addingService(ServiceReference<NetconfOperationServiceFactory> reference) {
         NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference);
-        operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
+        factoriesListener.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory);
         return netconfOperationServiceFactory;
     }
 
     @Override
     public void removedService(ServiceReference<NetconfOperationServiceFactory> reference,
             NetconfOperationServiceFactory netconfOperationServiceFactory) {
-        operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
+        factoriesListener.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory);
     }
 
 }
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 (file)
index 0000000..7a0b8b7
--- /dev/null
@@ -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);
+}
index b27cd2017292cd3995ff50db08ba952a44c4a03c..fca3e8bc1bd496f251687e7277beae6ec5020af9 100644 (file)
@@ -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 (file)
index 0000000..2f8fac2
--- /dev/null
@@ -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);
+    }
+}
index c0d2687a8a092b376f498e5cccadcaadfb816ab4..ce5233c494e6ddac78339709e9ae23dc5df05658 100644 (file)
@@ -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);
 
index eec96592d8e4e6e72e701a5596de52e17a1d7738..0140b65c146e4389ecc92e117d23685740ad3f4c 100644 (file)
@@ -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);
 
 
index 410d9a96aa4a254b043901e74d3e19d8b049ead3..f2de91ef46d2be313b7626262076463fa8cc687d 100644 (file)
             <artifactId>netconf-impl</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-monitoring</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>netconf-mapping-api</artifactId>
index a2b87c113c2a2f9e3b5028edefadafb69863e880..91b543b4de90b2410501e91ddcf8d160d986149b 100644 (file)
@@ -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);
index 9483785031b9c6fee67dbb8f5d4904767b5cd49a..ccc7c85eb39e38d345d710e50a42aed2d485df0b 100644 (file)
@@ -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.<NetconfOperationService>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 (file)
index 0000000..244e4ba
--- /dev/null
@@ -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<InputStream> yangDependencies = NetconfITTest.getBasicYangs();
+        return new HardcodedYangStoreService(yangDependencies);
+    }
+
+    private NetconfServerDispatcher createDispatcher(
+                                                     NetconfOperationServiceFactoryListenerImpl factoriesListener) {
+        SessionIdProvider idProvider = new SessionIdProvider();
+        NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+                new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider);
+
+        NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory(
+                factoriesListener, commitNot, idProvider, getNetconfMonitoringListenerService(logger, monitoringService));
+
+        NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+                serverNegotiatorFactory, listenerFactory);
+        return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+    }
+
+    static SessionMonitoringService getNetconfMonitoringListenerService(final Logger logger, final NetconfMonitoringServiceImpl monitor) {
+        return new SessionMonitoringService() {
+            @Override
+            public void onSessionUp(NetconfManagementSession session) {
+                logger.debug("Management session up {}", session);
+                monitor.onSessionUp(session);
+            }
+
+            @Override
+            public void onSessionDown(NetconfManagementSession session) {
+                logger.debug("Management session down {}", session);
+                monitor.onSessionDown(session);
+            }
+        };
+    }
+
+
+    @Test
+    public void testGetResponseFromMonitoring() throws Exception {
+        try (NetconfClient netconfClient = new NetconfClient("client-monitoring", tcpAddress, 4000, clientDispatcher)) {
+        try (NetconfClient netconfClient2 = new NetconfClient("client-monitoring2", tcpAddress, 4000, clientDispatcher)) {
+            NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+            assertSessionElementsInResponse(response.getDocument(), 2);
+        }
+            NetconfMessage response = netconfClient.sendMessage(loadGetMessage());
+            assertSessionElementsInResponse(response.getDocument(), 1);
+        }
+    }
+
+
+    @Test(timeout = 5 * 10000)
+    public void testClientHelloWithAuth() throws Exception {
+        String fileName = "netconfMessages/client_hello_with_auth.xml";
+        String hello = XmlFileLoader.fileToString(fileName);
+
+        fileName = "netconfMessages/get.xml";
+        String get = XmlFileLoader.fileToString(fileName);
+
+        Socket sock = new Socket(tcpAddress.getHostName(), tcpAddress.getPort());
+        sock.getOutputStream().write(hello.getBytes(Charsets.UTF_8));
+        String separator = "]]>]]>";
+
+        sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+        sock.getOutputStream().write(get.getBytes(Charsets.UTF_8));
+        sock.getOutputStream().write(separator.getBytes(Charsets.UTF_8));
+
+        StringBuilder responseBuilder = new StringBuilder();
+
+        try (InputStream inputStream = sock.getInputStream();
+             InputStreamReader reader = new InputStreamReader(inputStream);
+             BufferedReader buff = new BufferedReader(reader)) {
+            String line;
+            while ((line = buff.readLine()) != null) {
+
+                responseBuilder.append(line);
+                responseBuilder.append(System.lineSeparator());
+                System.out.println(responseBuilder.toString());
+
+                if(line.contains("</rpc-reply>"))
+                    break;
+            }
+        }
+
+        org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>"));
+        org.junit.Assert.assertThat(responseBuilder.toString(), JUnitMatchers.containsString("<username>tomas</username>"));
+    }
+
+    private void assertSessionElementsInResponse(Document document, int i) {
+        int elementSize = document.getElementsByTagName("session-id").getLength();
+        Assert.assertEquals(i, elementSize);
+    }
+
+    private NetconfMessage loadGetMessage() throws Exception {
+        return XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/get.xml");
+    }
+
+    public NetconfOperationServiceFactoryListener getFactoriesListener() {
+        NetconfOperationServiceFactoryListener factoriesListener = mock(NetconfOperationServiceFactoryListener.class);
+        NetconfOperationServiceSnapshot snap = mock(NetconfOperationServiceSnapshot.class);
+        NetconfOperationService service = mock(NetconfOperationService.class);
+        Set<Capability> caps = Sets.newHashSet();
+        caps.add(new Capability() {
+            @Override
+            public String getCapabilityUri() {
+                return "namespaceModuleRevision";
+            }
+
+            @Override
+            public Optional<String> getModuleNamespace() {
+                return Optional.of("namespace");
+            }
+
+            @Override
+            public Optional<String> getModuleName() {
+                return Optional.of("name");
+            }
+
+            @Override
+            public Optional<String> getRevision() {
+                return Optional.of("revision");
+            }
+
+            @Override
+            public Optional<String> getCapabilitySchema() {
+                return Optional.of("content");
+            }
+
+            @Override
+            public Optional<List<String>> getLocation() {
+                return Optional.absent();
+            }
+        });
+
+        doReturn(caps).when(service).getCapabilities();
+        Set<NetconfOperationService> services = Sets.newHashSet(service);
+        doReturn(services).when(snap).getServices();
+        doReturn(snap).when(factoriesListener).getSnapshot(anyLong());
+
+        return factoriesListener;
+    }
+
+
+}
index 6351c617cf505aad255eb90ee9921d46c53643bf..3dc7155317dbf769c3acdc0edc56a873e6e8dfaa 100644 (file)
@@ -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<String> getModuleNamespace();
+
     public Optional<String> getModuleName();
 
     public Optional<String> getRevision();
 
     public Optional<String> getCapabilitySchema();
+
+    public Optional<List<String>> getLocation();
 }
diff --git a/opendaylight/netconf/netconf-monitoring/pom.xml b/opendaylight/netconf/netconf-monitoring/pom.xml
new file mode 100644 (file)
index 0000000..31e4271
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>netconf-subsystem</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <version>0.2.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>netconf-monitoring</artifactId>
+    <name>${project.artifactId}</name>
+    <packaging>bundle</packaging>
+
+
+    <dependencies>
+        <!-- compile dependencies -->
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-util</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-mapping-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.yangtools.model</groupId>
+            <artifactId>ietf-inet-types</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.opendaylight.bgpcep</groupId>
+            <artifactId>mockito-configuration</artifactId>
+            <version>${bgpcep.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-Activator>org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringActivator</Bundle-Activator>
+                        <Import-Package>
+                            com.google.common.base,
+                            com.google.common.collect,
+                            com.google.common.io,
+                            org.opendaylight.controller.netconf.api,
+                            org.opendaylight.controller.netconf.mapping.api,
+                            org.opendaylight.controller.netconf.util.mapping,
+                            org.osgi.framework,
+                            org.slf4j,
+                            org.w3c.dom,
+                            javax.xml.bind,
+                            javax.xml.bind.annotation,
+                            javax.xml.transform,
+                            javax.xml.transform.dom,
+                            org.opendaylight.controller.netconf.util.xml,
+                            io.netty.util.internal,
+                            javax.annotation,
+                            org.opendaylight.controller.netconf.api.monitoring,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924,
+                            org.osgi.util.tracker,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.sessions,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004,
+                            org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas,
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
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 (file)
index 0000000..0b923f6
--- /dev/null
@@ -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<String, String> info = Maps.newHashMap();
+            info.put(NetconfDocumentedException.ErrorSeverity.error.toString(), e.getMessage());
+            throw new NetconfDocumentedException(errorMessage, NetconfDocumentedException.ErrorType.application,
+                    NetconfDocumentedException.ErrorTag.operation_failed,
+                    NetconfDocumentedException.ErrorSeverity.error, info);
+        }
+    }
+
+    private Element getPlaceholder(Document innerResult) {
+        try {
+            XmlElement rootElement = XmlElement.fromDomElementWithExpected(innerResult.getDocumentElement(),
+                    XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.RFC4741_TARGET_NAMESPACE);
+            return rootElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY).getDomElement();
+        } catch (RuntimeException e) {
+            throw new IllegalArgumentException(String.format(
+                    "Input xml in wrong format, Expecting root element %s with child element %s, but was %s",
+                    XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.DATA_KEY,
+                    XmlUtil.toString(innerResult.getDocumentElement())), e);
+        }
+    }
+
+    private boolean canHandle(AbstractNetconfOperation.OperationNameAndNamespace operationNameAndNamespace) {
+        if (operationNameAndNamespace.getOperationName().equals(XmlNetconfConstants.GET) == false)
+            return false;
+        return operationNameAndNamespace.getNamespace().equals(
+                XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+    }
+
+    @Override
+    public int getSortingOrder() {
+        // FIXME filters for different operations cannot have same order
+        return 1;
+    }
+
+    @Override
+    public int compareTo(NetconfOperationFilter o) {
+        return Integer.compare(getSortingOrder(), o.getSortingOrder());
+    }
+
+}
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 (file)
index 0000000..3a9d3ba
--- /dev/null
@@ -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 (file)
index 0000000..1143231
--- /dev/null
@@ -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 (file)
index 0000000..fe01847
--- /dev/null
@@ -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<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+
+        @Override
+        public String getCapabilityUri() {
+            return MonitoringConstants.URI;
+        }
+
+        @Override
+        public Optional<String> getModuleNamespace() {
+            return Optional.of(MonitoringConstants.NAMESPACE);
+        }
+
+        @Override
+        public Optional<String> getModuleName() {
+            return Optional.of(MonitoringConstants.MODULE_NAME);
+        }
+
+        @Override
+        public Optional<String> getRevision() {
+            return Optional.of(MonitoringConstants.MODULE_REVISION);
+        }
+
+        @Override
+        public Optional<String> getCapabilitySchema() {
+            return Optional.absent();
+        }
+
+        @Override
+        public Optional<List<String>> getLocation() {
+            return Optional.absent();
+        }
+    });
+
+    private final NetconfMonitoringService monitor;
+
+    public NetconfMonitoringOperationService(NetconfMonitoringService monitor) {
+        this.monitor = monitor;
+    }
+
+    private static String readSchema() {
+        String schemaLocation = "/META-INF/yang/ietf-netconf-monitoring.yang";
+        URL resource = Schemas.class.getClassLoader().getResource(schemaLocation);
+        Preconditions.checkNotNull(resource, "Unable to read schema content from %s", schemaLocation);
+        File file = new File(resource.getFile());
+        try {
+            return Files.toString(file, Charsets.UTF_8);
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to load schema from " + schemaLocation, e);
+        }
+    }
+
+    @Override
+    public Set<Capability> getCapabilities() {
+        return CAPABILITIES;
+    }
+
+    @Override
+    public Set<NetconfOperation> getNetconfOperations() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Set<NetconfOperationFilter> getFilters() {
+        return Sets.<NetconfOperationFilter>newHashSet(new Get(monitor));
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
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 (file)
index 0000000..920236b
--- /dev/null
@@ -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<NetconfMonitoringService, NetconfMonitoringService> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfMonitoringServiceTracker.class);
+
+    private ServiceRegistration<NetconfOperationServiceFactory> reg;
+
+    NetconfMonitoringServiceTracker(BundleContext context) {
+        super(context, NetconfMonitoringService.class, null);
+    }
+
+    @Override
+    public NetconfMonitoringService addingService(ServiceReference<NetconfMonitoringService> reference) {
+        Preconditions.checkState(reg == null, "Monitoring service was already added");
+
+        NetconfMonitoringService netconfMonitoringService = super.addingService(reference);
+
+        final NetconfMonitoringOperationService operationService = new NetconfMonitoringOperationService(
+                netconfMonitoringService);
+        NetconfOperationServiceFactory factory = new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+                operationService);
+
+        Dictionary<String, ?> props = new Hashtable<>();
+        reg = context.registerService(NetconfOperationServiceFactory.class, factory, props);
+
+        return netconfMonitoringService;
+    }
+
+    @Override
+    public void removedService(ServiceReference<NetconfMonitoringService> reference,
+            NetconfMonitoringService netconfMonitoringService) {
+        if(reg!=null) {
+            try {
+                reg.unregister();
+            } catch (Exception e) {
+                logger.warn("Ignoring exception while unregistering {}", reg, e);
+            }
+        }
+    }
+
+}
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 (file)
index 0000000..4b07ab0
--- /dev/null
@@ -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 (file)
index 0000000..078509d
--- /dev/null
@@ -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<String> getLocation() {
+        return Collections2.transform(schema.getLocation(), new Function<Schema.Location, String>() {
+            @Nullable
+            @Override
+            public String apply(@Nullable Schema.Location input) {
+                return input.getEnumeration().toString();
+            }
+        });
+    }
+
+    @XmlElement(name = "version")
+    public String getVersion() {
+        return schema.getVersion();
+    }
+
+    @XmlElement(name = "format")
+    public String getFormat() {
+        Preconditions.checkState(schema.getFormat() == Yang.class, "Only yang format permitted, but was %s", schema.getFormat());
+        return "yang";
+    }
+}
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 (file)
index 0000000..25fb5d4
--- /dev/null
@@ -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 (file)
index 0000000..98bda58
--- /dev/null
@@ -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<MonitoringSchema> getSchemas() {
+        return Collections2.transform(schemas.getSchema(), new Function<Schema, MonitoringSchema>() {
+            @Nullable
+            @Override
+            public MonitoringSchema apply(@Nullable Schema input) {
+                return new MonitoringSchema(input);
+            }
+        });
+    }
+
+    @XmlElementWrapper(name="sessions")
+    @XmlElement(name="session")
+    public Collection<MonitoringSession> getSessions() {
+        return Collections2.transform(sessions.getSession(), new Function<Session, MonitoringSession>() {
+            @Nullable
+            @Override
+            public MonitoringSession apply(@Nullable Session input) {
+                return new MonitoringSession(input);
+            }
+        });
+    }
+}
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 (file)
index 0000000..8771421
--- /dev/null
@@ -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 (file)
index 0000000..cb6e59f
--- /dev/null
@@ -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.<Schema>newArrayList()).build();
+            }
+        };
+        NetconfState model = new NetconfState(service);
+        Element xml = new JaxBSerializer().toXml(model);
+        System.out.println(XmlUtil.toString(xml));
+    }
+
+    private Session getMockSession() {
+        Session mocked = mock(Session.class);
+        doReturn(1L).when(mocked).getSessionId();
+        doReturn(new DateAndTime(new Date().toString())).when(mocked).getLoginTime();
+        doReturn(new Host(new DomainName("address/port"))).when(mocked).getSourceHost();
+        doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInBadRpcs();
+        doReturn(new ZeroBasedCounter32(0L)).when(mocked).getInRpcs();
+        doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutNotifications();
+        doReturn(new ZeroBasedCounter32(0L)).when(mocked).getOutRpcErrors();
+        doReturn(NetconfSsh.class).when(mocked).getTransport();
+        doReturn("username").when(mocked).getUsername();
+        return mocked;
+    }
+}
index 3ed75a1b1684af1085347b9e822479720a5da83e..95d2feb65c482ea564bce7794d61d6360e35f379 100644 (file)
@@ -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<P extends NetconfSessionP
                 channel.pipeline().addAfter("aggregator", "chunkDecoder", new NetconfMessageChunkDecoder());
             }
             changeState(State.ESTABLISHED);
-            S session = getSession(sessionListener, channel, doc);
+            S session = getSession(sessionListener, channel, netconfMessage);
             negotiationSuccessful(session);
         } else {
             final IllegalStateException cause = new IllegalStateException(
                     "Received message was not hello as expected, but was " + XmlUtil.toString(doc));
+            logger.warn("Negotiation of netconf session failed", cause);
             negotiationFailed(cause);
         }
     }
 
-    protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc);
+    protected abstract S getSession(SessionListener sessionListener, Channel channel, NetconfMessage message);
 
     private boolean isHelloMessage(Document doc) {
         try {
index 0b6914ec2ae44f12f5c4ade5a9c9574c7b2a7de6..891d40cf74fa520be681aa73109765424f5ab8e8 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.netconf.util.messages;
 
 import com.google.common.base.Charsets;
 import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
 import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -27,6 +28,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * NetconfMessageFactory for (de)serializing DOM documents.
@@ -47,26 +49,65 @@ public final class NetconfMessageFactory implements ProtocolMessageFactory<Netco
 
     @Override
     public NetconfMessage parse(byte[] bytes) throws DeserializerException, DocumentedException {
-        String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
-        logger.debug("Parsing message \n{}", s);
-        if (bytes[0] == '[') {
-            // yuma sends auth information in the first line. Ignore until ]\n
-            // is found.
-            int endOfAuthHeader = ByteArray.findByteSequence(bytes, new byte[] { ']', '\n' });
+        logMessage(bytes);
+
+        String additionalHeader = null;
+
+        if (startsWithAdditionalHeader(bytes)) {
+            // Auth information containing username, ip address... extracted for monitoring
+            int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
             if (endOfAuthHeader > -1) {
+                byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
+                additionalHeader = additionalHeaderToString(additionalHeaderBytes);
                 bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
             }
         }
-        NetconfMessage message = null;
+        NetconfMessage message;
         try {
             Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
-            message = new NetconfMessage(doc);
+            message = new NetconfMessage(doc, additionalHeader);
         } catch (final SAXException | IOException | IllegalStateException e) {
             throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
         }
         return message;
     }
 
+    private int getAdditionalHeaderEndIndex(byte[] bytes) {
+        for (String possibleEnd : Lists.newArrayList("]\n", "]\r\n")) {
+            int idx = ByteArray.findByteSequence(bytes, possibleEnd.getBytes(Charsets.UTF_8));
+
+            if (idx != -1) {
+                return idx;
+            }
+        }
+
+        return -1;
+    }
+
+    private boolean startsWithAdditionalHeader(byte[] bytes) {
+        List<String> possibleStarts = Lists.newArrayList("[", "\r\n[", "\n[");
+        for (String possibleStart : possibleStarts) {
+            int i = 0;
+            for (byte b : possibleStart.getBytes(Charsets.UTF_8)) {
+                if(bytes[i]!=b)
+                    break;
+
+                return true;
+            }
+        }
+
+        return false;
+    };
+
+    private void logMessage(byte[] bytes) {
+        String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+        logger.debug("Parsing message \n{}", s);
+    }
+
+    private String additionalHeaderToString(byte[] bytes) {
+        return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
+    }
+
     @Override
     public byte[] put(NetconfMessage netconfMessage) {
         if (clientId.isPresent()) {
index cb3606cb4cb06fd7f6535728509b84988c6dd784..a43216917befd447ec4a6aedb1b82b31934e1633 100644 (file)
@@ -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;
index 7fb293f054e40aff5e3d840488a4b3b9047e8110..c410cf30b0abd00290bbc178c5ea3c9cbc7c0a21 100644 (file)
@@ -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 (file)
index 0000000..f8c9083
--- /dev/null
@@ -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));
+
+    }
+}
index 174640c4f9164d92fc8ff6ef68f3c805655d3d14..c0aaf8f23dc83b482cb6095159231dcb84b32a91 100644 (file)
@@ -1,8 +1,6 @@
 [tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;]
-        <?xml version="1.0" encoding="UTF-8"?>
-<hello
-        xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
     <capabilities>
         <capability>urn:ietf:params:netconf:base:1.0</capability>
     </capabilities>
-</hello>
+</hello>
\ No newline at end of file
index ad8356431ecc4777bfee0bd1b1e7717c1a7af039..52b7370e35fa51d97806a66062a280ca595ff80a 100644 (file)
@@ -29,6 +29,8 @@
         <module>netconf-ssh</module>
         <module>../../third-party/ganymed</module>
         <module>../../third-party/com.siemens.ct.exi</module>
+        <module>netconf-monitoring</module>
+        <module>ietf-netconf-monitoring</module>
     </modules>
 
     <profiles>
                 <artifactId>netconf-impl</artifactId>
                 <version>${netconf.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-monitoring</artifactId>
+                <version>${netconf.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-monitoring</artifactId>
+                <version>${netconf.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.opendaylight.controller</groupId>
                 <artifactId>config-persister-api</artifactId>