This commit consists of:
1) Ability for the netconf client to retrieve schemas metadata from
   the netconf server
2) Ability for the netconf server to retrieve specific yang schema from
   the netconf server
3) Netconf says Happy New Year 2017

Change-Id: I6552224707607ca6cc1397f2fbf193503bb116a3
diff --git a/netconf/__init__.py b/netconf/__init__.py
index 7398217..e69de29 100644
--- a/netconf/__init__.py
+++ b/netconf/__init__.py
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-#
-#
-# December 23 2014, Christian Hopps <chopps@gmail.com>
-#
-# Copyright (c) 2015, Deutsche Telekom AG
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-from __future__ import absolute_import, division, unicode_literals, print_function, nested_scopes
-from lxml.etree import register_namespace
-
-MAXSSHBUF = 16 * 1024
-NSMAP = { }
-
-
-def nsmap_add (prefix, namespace):
-    "Add a prefix namespace mapping to the modules mapping dictionary"
-    NSMAP[prefix] = namespace
-    register_namespace(prefix, namespace)
-
-
-def nsmap_update (nsdict):
-    "Add a dicitonary of prefx namespace mappings to the modules mapping dictionary"
-    NSMAP.update(nsdict)
-    for key, val in nsdict.items():
-        register_namespace(key, val)
-
-
-def qmap (key):
-    return "{" + NSMAP[key] + "}"
-
-
-# Add base spec namespace
-nsmap_add('nc', "urn:ietf:params:xml:ns:netconf:base:1.0")
diff --git a/netconf/capabilities.py b/netconf/capabilities.py
index 78f5cf4..80fe63d 100755
--- a/netconf/capabilities.py
+++ b/netconf/capabilities.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,149 +14,67 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+import structlog
+import sys
 from constants import Constants as C
 
+# URN_PREFIX = "urn:ietf:params:netconf:capability:"
+URN_PREFIX = "urn:opencord:params:xml:ns:voltha:"
+log = structlog.get_logger()
+
 class Capabilities:
 
     def __init__(self):
-        self.server_caps = self._get_server_capabilities()
+        self.server_caps = set()
         self.client_caps = set()
+        self.voltha_schemas = set()
+        self.schema_dir = None
 
     def add_client_capability(self, cap):
         self.client_caps.add(cap)
 
-    #TODO:  This will be automatically generated from the voltha proto files
-    def _get_server_capabilities(self):
-        return (
-            C.NETCONF_BASE_10,
-            C.NETCONF_BASE_11,
-            "urn:ietf:params:netconf:capability:writable-running:1.0",
-            "urn:opencord:params:xml:ns:voltha:ietf-voltha",
-            "urn:opencord:params:xml:ns:voltha:ietf-openflow_13",
-            "urn:opencord:params:xml:ns:voltha:ietf-meta",
-            "urn:opencord:params:xml:ns:voltha:ietf-logical_device",
-            "urn:opencord:params:xml:ns:voltha:ietf-health",
-            "urn:opencord:params:xml:ns:voltha:ietf-device",
-            "urn:opencord:params:xml:ns:voltha:ietf-empty",
-            "urn:opencord:params:xml:ns:voltha:ietf-common",
-            "urn:opencord:params:xml:ns:voltha:ietf-any",
-            "urn:opencord:params:xml:ns:voltha:ietf-adapter"
-        )
-
-    #TODO:  A schema exchange will also need to happen
-
-    description = """
-
-    Option 1:  Client already have the yang model for voltha and adapters:
-        <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-            <capabilities>
-                <capability>
-                    urn:ietf:params:netconf:base:1.1
-                </capability>
-                <capability>
-                    urn:cord:voltha:1.0
-                </capability>
-                <capability>
-                    urn:cord:voltha:adpater_x:1.0
-                </capability>
+    def set_server_capabilities(self, schemas):
+        # prefix = "urn:ietf:params:netconf:capability:"
+        # first add the basic capabilities
+        self.server_caps.add(C.NETCONF_BASE_10)
+        self.server_caps.add(C.NETCONF_BASE_11)
+        self.server_caps.add(C.NETCONF_MONITORING)
+        for schema in schemas:
+            self.server_caps.add(''.join([URN_PREFIX, schema]))
+            self.voltha_schemas.add(schema)
 
 
-    Option 2: NETCONF-MONITORING - schema exchanges
+    def set_schema_dir(self, schema_dir)        :
+        self.schema_dir = schema_dir
 
-        server expose capabilities
+    def get_yang_schemas_definitions(self):
+        defs = []
+        for schema in self.voltha_schemas:
+            defs.append(
+                {
+                    'id': schema,
+                    'version': '2016-11-15', #TODO: need to extract from voltha
+                    'format': 'yang',
+                    'location': 'NETCONF',
+                    'namespace': ''.join([URN_PREFIX, schema])
+                 }
+            )
+        return defs
 
-            <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-                <capabilities>
-                    <capability>
-                        urn:ietf:params:netconf:base:1.1
-                    </capability>
-                    <capability>
-                        urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
-                    </capability>
-
-        client request schemas
-
-            <rpc message-id="101"
-                 xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
-                <get>
-                    <filter type="subtree">
-                        <netconf-state xmlns=
-                            "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
-                             <schemas/>
-                        </netconf-state>
-                    </filter>
-                </get>
-            </rpc>
-
-        server sends back schemas
-
-            <rpc-reply message-id="101"
-                       xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
-                  <data>
-                        <netconf-state
-                            xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
-                            <schemas>
-                                <schema>
-                                    <identifier>voltha</identifier>
-                                    <version>1.0</version>
-                                    <format>yang</format>
-                                    <namespace>urn:cord:voltha</namespace>
-                                    <location>NETCONF</location>
-                                </schema>
-                                <schema>
-                                    <identifier>adapter_x</identifier>
-                                    <version>x_release</version>
-                                    <format>yang</format>
-                                    <namespace>urn:cord:voltha:adapter_x</namespace>
-                                    <location>NETCONF</location>
-                                </schema>
-                            </schemas>
-                        </netconf-state>
-                  </data>
-            </rpc-reply>
+    def is_schema_supported(self, schema):
+        return schema in self.voltha_schemas
 
 
-        client requests each schema instance
+    def get_schema_content(self, schema):
+        if self.schema_dir not in sys.path:
+            sys.path.insert(0, self.schema_dir)
 
-            <rpc message-id="102"
-                xmlns="urn:ietf:params:xml:ns:netconf:base:1.1">
-                <get-schema
-                    xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
-                    <identifer>voltha</identifer>
-                    <version>1.0</version>
-                </get-schema>
-             </rpc>
+        try:
+            with open(''.join([self.schema_dir, '/', schema, '.yang']),
+                      'r') as f:
+                content = f.read()
+                return content
+        except Exception as e:
+            log.error("error-opening-file", file=''.join([schema, '.yang']),
+                      dir=self.schema_dir, exception=repr(e))
 
-             <rpc-reply message-id="102"
-                xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-                <data
-                    xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
-                    module voltha {
-                        //default format (yang) returned
-                        //voltha version 0.1 yang module
-                        //contents here ...
-                    }
-                </data>
-             </rpc-reply>
-
-
-    GETTING DATA
-
-    Use filter:
-        1) namespace filter
-            <filter type="subtree">
-                <top xmlns="http://example.com/schema/1.2/config"/>
-            </filter>
-
-         2) <filter type="subtree">
-                <adapters xmlns="urn:cord:voltha:adapter_x">
-                    <adapter>
-                        <id>uuid</id>
-                        <config/>
-                    </adapter>
-                </adapters>
-            </filter>
-
-            /voltha/adapters/<adapter>/[<id>, <vendor>, <version>, <config>, <additonal_desc>]
-
-    """
\ No newline at end of file
diff --git a/netconf/connection_mgr.py b/netconf/connection_mgr.py
index 20dc184..32bbea4 100644
--- a/netconf/connection_mgr.py
+++ b/netconf/connection_mgr.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/netconf/constants.py b/netconf/constants.py
index e59a73e..332ec16 100644
--- a/netconf/constants.py
+++ b/netconf/constants.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,6 +19,9 @@
 
 	SSH_SUBSYSTEM = "netconf"
 
+	# Send message max size
+	MAXSSHBUF = 1024 * 1024
+
 	# Secure credentials directories
 	# TODO:  In a production environment these locations require better
 	# protection.  For now the user_passwords file is just a plain text file.
@@ -80,6 +83,8 @@
 
 	#tags
 	NC = "nc"
+	VOLTHA = 'voltha'
+	NCM = "ncm"
 	RPC = "rpc"
 	RPC_REPLY = "rpc-reply"
 	RPC_ERROR = "rpc-error"
@@ -96,3 +101,11 @@
 	MESSAGE_ID = "message-id"
 	XMLNS = "xmlns"
 	DELIMITER = "]]>]]>"
+
+	NS_MAP = {
+		'nc': 'urn:ietf:params:xml:ns:netconf:base:1.0',
+		'voltha': 'urn:opencord:params:xml:ns:voltha:ietf-voltha',
+		'ncm': 'urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring'
+	}
+
+
diff --git a/netconf/grpc_client/grpc_client.py b/netconf/grpc_client/grpc_client.py
index bce6de3..159abc2 100644
--- a/netconf/grpc_client/grpc_client.py
+++ b/netconf/grpc_client/grpc_client.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -76,6 +76,8 @@
         self.plugin_dir = os.path.abspath(os.path.join(
             os.path.dirname(__file__), '../protoc_plugins'))
 
+        self.yang_schemas = set()
+
         self.channel = None
         self.local_stub = None
         self.schema = None
@@ -141,6 +143,8 @@
                 yang_from = self._retrieve_schema()
                 log.info('proto-to-yang-schema', file=yang_from)
                 self._compile_proto_files(yang_from)
+                self._set_yang_schemas()
+
                 self._clear_backoff()
 
                 if self.on_start_callback is not None:
@@ -280,6 +284,20 @@
 
             # TODO: find a different way to test the generated yang files
 
+    def _set_yang_schemas(self):
+        if self.work_dir not in sys.path:
+            sys.path.insert(0, self.work_dir)
+
+        for fname in [f for f in os.listdir(self.work_dir)
+                      if f.endswith('.yang')]:
+            # Special case : since ietf-http, ietf-annotations,
+            # ietf-yang_options are not used for yang schema then do not add
+            # them to the set
+            if fname not in ['ietf-http.yang', 'ietf-yang_options.yang',
+                             'ietf-descriptor.yang']:
+                self.yang_schemas.add(fname[:-len('.yang')])
+        log.info('yang-schemas', schemas=self.yang_schemas)
+
     # TODO: should be generated code
     # Focus for now is issuing a GET request for VolthaGlobalService or VolthaLocalService
     @inlineCallbacks
diff --git a/netconf/main.py b/netconf/main.py
index e049433..7d99f3e 100755
--- a/netconf/main.py
+++ b/netconf/main.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/netconf/nc_common/__init__.py b/netconf/nc_common/__init__.py
index 7398217..e69de29 100644
--- a/netconf/nc_common/__init__.py
+++ b/netconf/nc_common/__init__.py
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-#
-#
-# December 23 2014, Christian Hopps <chopps@gmail.com>
-#
-# Copyright (c) 2015, Deutsche Telekom AG
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-from __future__ import absolute_import, division, unicode_literals, print_function, nested_scopes
-from lxml.etree import register_namespace
-
-MAXSSHBUF = 16 * 1024
-NSMAP = { }
-
-
-def nsmap_add (prefix, namespace):
-    "Add a prefix namespace mapping to the modules mapping dictionary"
-    NSMAP[prefix] = namespace
-    register_namespace(prefix, namespace)
-
-
-def nsmap_update (nsdict):
-    "Add a dicitonary of prefx namespace mappings to the modules mapping dictionary"
-    NSMAP.update(nsdict)
-    for key, val in nsdict.items():
-        register_namespace(key, val)
-
-
-def qmap (key):
-    return "{" + NSMAP[key] + "}"
-
-
-# Add base spec namespace
-nsmap_add('nc', "urn:ietf:params:xml:ns:netconf:base:1.0")
diff --git a/netconf/nc_common/error.py b/netconf/nc_common/error.py
index 802cbe9..bb28490 100644
--- a/netconf/nc_common/error.py
+++ b/netconf/nc_common/error.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Adapted from https://github.com/choppsv1/netconf/error.py
 #
diff --git a/netconf/nc_common/utils.py b/netconf/nc_common/utils.py
new file mode 100644
index 0000000..3af31bd
--- /dev/null
+++ b/netconf/nc_common/utils.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 the original author or authors.
+#
+# Code adapted from https://github.com/choppsv1/netconf
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from netconf.constants import Constants as C
+from lxml import etree
+
+ns_map = C.NS_MAP
+
+def qmap(key):
+    if ns_map.has_key(key):
+        return ''.join(['{', ns_map[key], '}'])
+
+def ns(key):
+    if ns_map.has_key(key):
+        return ns_map[key]
+
+def qname(tag):
+    try:
+        return etree.QName(tag)
+    except ValueError:
+        prefix, base = tag.split(":")
+        return etree.QName(ns(prefix), base)
+
+def elm(tag, attrib=None, **extra):
+    if attrib is None:
+        attrib = dict()
+    return etree.Element(qname(tag), attrib, **extra)
+
diff --git a/netconf/nc_rpc/__init__.py b/netconf/nc_rpc/__init__.py
index 7398217..e69de29 100644
--- a/netconf/nc_rpc/__init__.py
+++ b/netconf/nc_rpc/__init__.py
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-#
-#
-# December 23 2014, Christian Hopps <chopps@gmail.com>
-#
-# Copyright (c) 2015, Deutsche Telekom AG
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-from __future__ import absolute_import, division, unicode_literals, print_function, nested_scopes
-from lxml.etree import register_namespace
-
-MAXSSHBUF = 16 * 1024
-NSMAP = { }
-
-
-def nsmap_add (prefix, namespace):
-    "Add a prefix namespace mapping to the modules mapping dictionary"
-    NSMAP[prefix] = namespace
-    register_namespace(prefix, namespace)
-
-
-def nsmap_update (nsdict):
-    "Add a dicitonary of prefx namespace mappings to the modules mapping dictionary"
-    NSMAP.update(nsdict)
-    for key, val in nsdict.items():
-        register_namespace(key, val)
-
-
-def qmap (key):
-    return "{" + NSMAP[key] + "}"
-
-
-# Add base spec namespace
-nsmap_add('nc', "urn:ietf:params:xml:ns:netconf:base:1.0")
diff --git a/netconf/nc_rpc/base/close_session.py b/netconf/nc_rpc/base/close_session.py
index 7e76b45..45a6cc2 100644
--- a/netconf/nc_rpc/base/close_session.py
+++ b/netconf/nc_rpc/base/close_session.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,8 +24,10 @@
 
 class CloseSession(Rpc):
 
-    def __init__(self, request, grpc_client, session):
-        super(CloseSession, self).__init__(request, grpc_client, session)
+    def __init__(self, request, request_xml, grpc_client, session,
+                 capabilities):
+        super(CloseSession, self).__init__(request, request_xml, grpc_client,
+                                           session)
         self._validate_parameters()
 
     def execute(self):
diff --git a/netconf/nc_rpc/base/commit.py b/netconf/nc_rpc/base/commit.py
index 06c1746..22a0e6a 100644
--- a/netconf/nc_rpc/base/commit.py
+++ b/netconf/nc_rpc/base/commit.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,8 +24,8 @@
 
 class Commit(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(Commit, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(Commit, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/copy_config.py b/netconf/nc_rpc/base/copy_config.py
index a59081d..1d96b76 100644
--- a/netconf/nc_rpc/base/copy_config.py
+++ b/netconf/nc_rpc/base/copy_config.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
 
 class CopyConfig(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(CopyConfig, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(CopyConfig, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/delete_config.py b/netconf/nc_rpc/base/delete_config.py
index 8ad5ea4..e21d2d4 100644
--- a/netconf/nc_rpc/base/delete_config.py
+++ b/netconf/nc_rpc/base/delete_config.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
 
 class DeleteConfig(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(DeleteConfig, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(DeleteConfig, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/discard_changes.py b/netconf/nc_rpc/base/discard_changes.py
index 961eea0..4b4b219 100644
--- a/netconf/nc_rpc/base/discard_changes.py
+++ b/netconf/nc_rpc/base/discard_changes.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
 
 class DiscardChanges(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(DiscardChanges, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(DiscardChanges, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/edit_config.py b/netconf/nc_rpc/base/edit_config.py
index 92fa085..0991c67 100644
--- a/netconf/nc_rpc/base/edit_config.py
+++ b/netconf/nc_rpc/base/edit_config.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
 
 class EditConfig(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(EditConfig, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(EditConfig, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/get.py b/netconf/nc_rpc/base/get.py
index 71449ad..d953b17 100644
--- a/netconf/nc_rpc/base/get.py
+++ b/netconf/nc_rpc/base/get.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,22 +14,18 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-from lxml import etree
 import structlog
 from netconf.nc_rpc.rpc import Rpc
 import netconf.nc_common.error as ncerror
-from netconf.constants import Constants as C
-from netconf.utils import filter_tag_match
 from twisted.internet.defer import inlineCallbacks, returnValue
 import dicttoxml
-from simplejson import dumps, load
 
 log = structlog.get_logger()
 
 
 class Get(Rpc):
-    def __init__(self, request, grpc_client, session):
-        super(Get, self).__init__(request, grpc_client, session)
+    def __init__(self, request, request_xml, grpc_client, session, capabilities):
+        super(Get, self).__init__(request, request_xml, grpc_client, session)
         self._validate_parameters()
 
     @inlineCallbacks
diff --git a/netconf/nc_rpc/base/get_config.py b/netconf/nc_rpc/base/get_config.py
index 2fb8ad0..e7ade72 100644
--- a/netconf/nc_rpc/base/get_config.py
+++ b/netconf/nc_rpc/base/get_config.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,20 +14,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-from lxml import etree
 import structlog
 from netconf.nc_rpc.rpc import Rpc
 import netconf.nc_common.error as ncerror
 from netconf.constants import Constants as C
-from netconf.utils import elm
-from netconf import NSMAP
 
 log = structlog.get_logger()
 
 class GetConfig(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(GetConfig, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(GetConfig, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
@@ -47,17 +44,17 @@
 			self.rpc_response.node = ncerror.BadMsg(self.rpc_request)
 			return
 
-		self.source_param = self.rpc_method.find(C.NC_SOURCE, namespaces=NSMAP)
-		if self.source_param is None:
-			self.rpc_response.is_error = True
-			self.rpc_response.node = ncerror.MissingElement(
-				self.rpc_request, elm(C.NC_SOURCE))
-			return
+		self.source_param = self.rpc_method.find(C.NC_SOURCE, namespaces=C.NS_MAP)
+		# if self.source_param is None:
+		# 	self.rpc_response.is_error = True
+		# 	self.rpc_response.node = ncerror.MissingElement(
+		# 		self.rpc_request, elm(C.NC_SOURCE))
+		# 	return
 
 		self.filter_param = None
 		if paramslen == 2:
 			self.filter_param = self.rpc_method.find(C.NC_FILTER,
-												  namespaces=NSMAP)
+												  namespaces=C.NS_MAP)
 			if self.filter_param is None:
 				unknown_elm = self.params[0] if self.params[0] != \
 												self.source_param else \
diff --git a/netconf/nc_rpc/base/kill_session.py b/netconf/nc_rpc/base/kill_session.py
index 16461cd..e10f3a5 100644
--- a/netconf/nc_rpc/base/kill_session.py
+++ b/netconf/nc_rpc/base/kill_session.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -25,8 +25,8 @@
 
 class KillSession(Rpc):
 
-    def __init__(self, request, grpc_client, session):
-        super(KillSession, self).__init__(request, grpc_client, session)
+    def __init__(self, request, request_xml, grpc_client, session, capabilities):
+        super(KillSession, self).__init__(request, request_xml, grpc_client, session)
         self._validate_parameters()
 
     def execute(self):
diff --git a/netconf/nc_rpc/base/lock.py b/netconf/nc_rpc/base/lock.py
index 2f41e24..5a59376 100644
--- a/netconf/nc_rpc/base/lock.py
+++ b/netconf/nc_rpc/base/lock.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
 
 class Lock(Rpc):
 
-	def __init__(self, request, grpc_client, session):
-		super(Lock, self).__init__(request, grpc_client, session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(Lock, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/unlock.py b/netconf/nc_rpc/base/unlock.py
index f9ef062..b5db7c1 100644
--- a/netconf/nc_rpc/base/unlock.py
+++ b/netconf/nc_rpc/base/unlock.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,9 +23,8 @@
 
 class UnLock(Rpc):
 
-	def __init__(self, rpc_request, rpc_method, grpc_client, session):
-		super(UnLock, self).__init__(rpc_request, rpc_method, grpc_client,
-									 session)
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
+		super(UnLock, self).__init__(request, request_xml, grpc_client, session)
 		self._validate_parameters()
 
 	def execute(self):
diff --git a/netconf/nc_rpc/base/validate.py b/netconf/nc_rpc/base/validate.py
index 8671b12..61e2f80 100644
--- a/netconf/nc_rpc/base/validate.py
+++ b/netconf/nc_rpc/base/validate.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
 
 class Validate(Rpc):
 
-	def __init__(self, request, grpc_client, session):
+	def __init__(self, request, request_xml, grpc_client, session, capabilities):
 		super(Validate, self).__init__(request, grpc_client, session)
 		self._validate_parameters()
 
diff --git a/netconf/nc_rpc/ext/get_schema.py b/netconf/nc_rpc/ext/get_schema.py
new file mode 100644
index 0000000..ec473d3
--- /dev/null
+++ b/netconf/nc_rpc/ext/get_schema.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from lxml import etree
+import structlog
+from netconf.nc_rpc.rpc import Rpc
+import netconf.nc_common.error as ncerror
+from netconf.constants import Constants as C
+from netconf.nc_common.utils import qmap, ns
+
+log = structlog.get_logger()
+
+class GetSchema(Rpc):
+    def __init__(self, request, request_xml, grpc_client, session, capabilities):
+        super(GetSchema, self).__init__(request, request_xml, grpc_client, session)
+        self.capabilities = capabilities
+        # specific schema parsing required
+        self.parse_schema_request(request_xml)
+        self._validate_parameters()
+
+    def execute(self):
+        if self.rpc_response.is_error:
+            return(self.rpc_response)
+
+        log.info('get-schema-request', session=self.session.session_id,
+                 request=self.request)
+
+        # Get the yang schema content
+        # TODO: Use version as well
+        content = self.capabilities.get_schema_content(self.request['identifier'])
+        if not content:
+            self.rpc_response.is_error = True
+            self.rpc_response.node = ncerror.BadMsg('Server Error')
+            return
+
+        self.rpc_response.node = self.create_xml_response(content)
+
+        self.rpc_response.is_error = False
+
+        return(self.rpc_response)
+
+    def _validate_parameters(self):
+        log.info('validate-parameters', session=self.session.session_id)
+        # Validate the GET command
+        if self.request:
+            try:
+                if self.request['command'] != 'get-schema' or \
+                        not self.request.has_key('identifier') or \
+                        not self.request.has_key('format') or \
+                        not self.request.has_key('version'):
+                    self.rpc_response.is_error = True
+                    self.rpc_response.node = ncerror.BadMsg('Improperly '
+                                                            'formatted get '
+                                                            'schemas request')
+                    return
+
+                if self.request.has_key('filter'):
+                    if not self.request.has_key('class'):
+                        self.rpc_response.is_error = True
+                        self.rpc_response.node = ncerror.BadMsg(
+                            'Missing filter sub-element')
+                        return
+
+                # Verify that the requested schema exists
+                if not self.capabilities.is_schema_supported(self.request[
+                                                             'identifier']) \
+                        or self.request['format'] != 'yang' :
+                    self.rpc_response.is_error = True
+                    self.rpc_response.node = ncerror.BadMsg(
+                        'Unsupported request')
+                    return
+
+            except Exception as e:
+                self.rpc_response.is_error = True
+                self.rpc_response.node = ncerror.BadMsg(self.request)
+                return
+
+    # Parse context-specific parameters
+    def parse_schema_request(self, node):
+        if not len(node):
+            return
+        schema_node = node.find(''.join([qmap(C.NCM), 'get-schema']))
+        if schema_node is not None:
+            for item in ['identifier', 'version', 'format']:
+                elem = schema_node.find(''.join([qmap(C.NCM), item]))
+                if elem is not None:
+                    self.request[item] = elem.text
+
+    def create_xml_response(self, content):
+        ns = {}
+        ns['xmlns'] = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
+
+        elem = etree.Element('data', attrib=ns)
+        elem.text = unicode(content, "utf-8")
+        return elem
diff --git a/netconf/nc_rpc/ext/get_schemas.py b/netconf/nc_rpc/ext/get_schemas.py
new file mode 100644
index 0000000..7ec4555
--- /dev/null
+++ b/netconf/nc_rpc/ext/get_schemas.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from lxml import etree
+import structlog
+from netconf.nc_rpc.rpc import Rpc
+import netconf.nc_common.error as ncerror
+
+log = structlog.get_logger()
+
+
+class GetSchemas(Rpc):
+    def __init__(self, request, request_xml, grpc_client, session, capabilities):
+        super(GetSchemas, self).__init__(request, request_xml, grpc_client, session)
+        self._validate_parameters()
+        self.capabilities = capabilities
+
+    def execute(self):
+        if self.rpc_response.is_error:
+            return(self.rpc_response)
+
+        log.info('get-schemas-request', session=self.session.session_id,
+                 request=self.request)
+
+        # Get the schema definitions
+        schema_defs = self.capabilities.get_yang_schemas_definitions()
+        log.info('schema-defs', definitions=schema_defs)
+
+        # format the schemas in xml
+        top = etree.Element('yang')
+        for dict in schema_defs:
+            schema = etree.SubElement(top, 'schema')
+            node = etree.SubElement(schema, 'identifier')
+            node.text = dict['id']
+            node = etree.SubElement(schema, 'version')
+            node.text = dict['version']
+            node = etree.SubElement(schema, 'format')
+            node.text = dict['format']
+            node = etree.SubElement(schema, 'namespace')
+            node.text = dict['namespace']
+            node = etree.SubElement(schema, 'location')
+            node.text = dict['location']
+
+        # Build the yang response
+        self.rpc_response.node = self.rpc_response.build_xml_response(
+            self.request, top)
+
+        self.rpc_response.is_error = False
+
+        return(self.rpc_response)
+
+
+    def _validate_parameters(self):
+        log.info('validate-parameters', session=self.session.session_id)
+        # Validate the GET command
+        if self.request:
+            try:
+                if self.request['command'] != 'get-schemas':
+                    self.rpc_response.is_error = True
+                    self.rpc_response.node = ncerror.BadMsg('Improperly '
+                                                            'formatted get '
+                                                            'schemas request')
+
+                if self.request.has_key('filter'):
+                    if not self.request.has_key('class'):
+                        self.rpc_response.is_error = True
+                        self.rpc_response.node = ncerror.BadMsg(
+                            'Missing filter sub-element')
+
+            except Exception as e:
+                self.rpc_response.is_error = True
+                self.rpc_response.node = ncerror.BadMsg(self.request)
+                return
+
diff --git a/netconf/nc_rpc/ext/get_voltha.py b/netconf/nc_rpc/ext/get_voltha.py
index 0c51e9a..5b004e7 100644
--- a/netconf/nc_rpc/ext/get_voltha.py
+++ b/netconf/nc_rpc/ext/get_voltha.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,20 +18,18 @@
 import structlog
 from netconf.nc_rpc.rpc import Rpc
 import netconf.nc_common.error as ncerror
-from netconf.constants import Constants as C
-from netconf.utils import filter_tag_match
 from twisted.internet.defer import inlineCallbacks, returnValue
 import dicttoxml
-from simplejson import dumps, load
 
 log = structlog.get_logger()
 
 
 class GetVoltha(Rpc):
-    def __init__(self, request, grpc_client, session):
-        super(GetVoltha, self).__init__(request, grpc_client, session)
+    def __init__(self, request, request_xml, grpc_client, session, capabilities):
+        super(GetVoltha, self).__init__(request, request_xml, grpc_client, session)
         self._validate_parameters()
 
+
     @inlineCallbacks
     def execute(self):
         log.info('get-voltha-request', session=self.session.session_id,
@@ -64,11 +62,5 @@
             self.rpc_response.node = ncerror.BadMsg(self.rpc_request)
             return
 
-        if self.params and not filter_tag_match(self.params[0], C.NC_FILTER):
-            self.rpc_response.is_error = True
-            self.rpc_response.node = ncerror.UnknownElement(
-                self.rpc_request, self.params[0])
-            return
-
         if not self.params:
             self.params = [None]
diff --git a/netconf/nc_rpc/rpc.py b/netconf/nc_rpc/rpc.py
index bafc58e..3f1eff4 100644
--- a/netconf/nc_rpc/rpc.py
+++ b/netconf/nc_rpc/rpc.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Code adapted from https://github.com/choppsv1/netconf
 #
@@ -24,8 +24,9 @@
 
 
 class Rpc(object):
-    def __init__(self, request, grpc_client, session):
-        self.request = request
+    def __init__(self, request_dict, request_xml, grpc_client, session):
+        self.request = request_dict
+        self.request_xml = request_xml
         self.rpc_response = RpcResponse()
         self.grpc_client =  grpc_client
         self.session = session
diff --git a/netconf/nc_rpc/rpc_factory.py b/netconf/nc_rpc/rpc_factory.py
index 230c57a..3d0d678 100644
--- a/netconf/nc_rpc/rpc_factory.py
+++ b/netconf/nc_rpc/rpc_factory.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Code adapted from https://github.com/choppsv1/netconf
 #
@@ -17,10 +17,10 @@
 # limitations under the License.
 #
 import structlog
+from netconf.constants import Constants as C
 from base.commit import Commit
 from base.copy_config import CopyConfig
 from base.delete_config import DeleteConfig
-from base.discard_changes import DiscardChanges
 from base.edit_config import EditConfig
 from base.get import Get
 from base.get_config import GetConfig
@@ -28,18 +28,15 @@
 from base.unlock import UnLock
 from base.close_session import CloseSession
 from base.kill_session import KillSession
+from ext.get_schemas import GetSchemas
+from ext.get_schema import GetSchema
 from ext.get_voltha import GetVoltha
-from netconf import NSMAP, qmap
 import netconf.nc_common.error as ncerror
-
-log = structlog.get_logger()
+from netconf.nc_common.utils import qmap, ns
 from lxml import etree
 
-ns_map = {
-    'base': '{urn:ietf:params:xml:ns:netconf:base:1.0}',
-    'voltha': '{urn:opencord:params:xml:ns:voltha:ietf-voltha}'
-}
 
+log = structlog.get_logger()
 
 class RpcFactory:
     instance = None
@@ -61,6 +58,7 @@
             if tup[0] == name:
                 return tup[1]
 
+
     # Parse a request (node is an ElementTree) and return a dictionary
     # TODO:  This parser is specific to a GET request.  Need to be it more
     # generic
@@ -69,8 +67,8 @@
         if not len(node):
             return request
         for elem in node.iter():
-            if elem.tag.find(ns_map['base']) != -1:  # found
-                elem_name = elem.tag.replace(ns_map['base'], "")
+            if elem.tag.find(qmap(C.NC)) != -1:  # found
+                elem_name = elem.tag.replace(qmap(C.NC), "")
                 if elem_name == 'rpc':
                     request['type'] = 'rpc'
                     request['message_id'] = self.get_attribute_value(
@@ -81,12 +79,24 @@
                 else:
                     request[
                         'command'] = elem_name  # attribute is empty for now
-            elif elem.tag.find(ns_map['voltha']) != -1:  # found
+            elif elem.tag.find(qmap(C.VOLTHA)) != -1:  # found
+                request['namespace'] = ns(C.VOLTHA)
                 if request.has_key('class'):
-                    request['subclass'] = elem.tag.replace(ns_map['voltha'],
-                                                           "")
+                    request['subclass'] = elem.tag.replace(qmap(C.VOLTHA),"")
                 else:
-                    request['class'] = elem.tag.replace(ns_map['voltha'], "")
+                    request['class'] = elem.tag.replace(qmap(C.VOLTHA), "")
+            elif elem.tag.find(qmap(C.NCM)) != -1:  # found
+                request['namespace'] = ns(C.NCM)
+                elem_name = elem.tag.replace(qmap(C.NCM), "")
+                if elem_name == 'get-schema':
+                    request['command'] = elem_name
+                    request['class'] = elem_name
+                elif request.has_key('class'):
+                    request['subclass'] = elem_name
+                elif elem_name == 'netconf-state':
+                    request['command'] = 'get-schemas'
+                    request['class'] = elem_name
+
         return request
 
     def register_rpc(self, namespace, service, name, klass):
@@ -99,7 +109,8 @@
         if key in self.rpc_map.keys():
             return self.rpc_map[key]
 
-    def get_rpc_handler(self, rpc_node, msg, grpc_channel, session):
+    def get_rpc_handler(self, rpc_node, msg, grpc_channel, session,
+                        capabilities):
         try:
             # Parse the request into a dictionary
             log.info("rpc-node",
@@ -110,17 +121,17 @@
                 log.error("request-bad-format")
                 raise ncerror.BadMsg(rpc_node)
 
-            if not request.has_key('message_id') or \
-                    not request.has_key('command'):
+            log.info("parsed-request", request=request)
+
+            if not request.has_key('message_id'):
                 log.error("request-no-message-id")
                 raise ncerror.BadMsg(rpc_node)
 
-            log.info("parsed-request", request=request)
-
             class_handler = self.rpc_class_handlers.get(request['command'],
                                                         None)
             if class_handler is not None:
-                return class_handler(request, grpc_channel, session)
+                return class_handler(request, rpc_node, grpc_channel, session,
+                                     capabilities)
 
             log.error("rpc-not-implemented", rpc=request['command'])
 
@@ -131,6 +142,8 @@
         'getvoltha': GetVoltha,
         'get-config': GetConfig,
         'get': Get,
+        'get-schemas': GetSchemas,
+        'get-schema': GetSchema,
         'edit-config': EditConfig,
         'copy-config': CopyConfig,
         'delete-config': DeleteConfig,
diff --git a/netconf/nc_rpc/rpc_response.py b/netconf/nc_rpc/rpc_response.py
index 584e35b..150631b 100644
--- a/netconf/nc_rpc/rpc_response.py
+++ b/netconf/nc_rpc/rpc_response.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Code adapted from https://github.com/choppsv1/netconf
 #
@@ -36,7 +36,7 @@
             return
         voltha_xml_string = etree.tostring(voltha_response)
 
-        # Remove the leading and trailing <root> tags
+        # Remove the leading and trailing <yang> tags
         if voltha_xml_string.startswith('<yang>'):
             voltha_xml_string = voltha_xml_string[len('<yang>'):]
             if voltha_xml_string.endswith('</yang>'):
@@ -48,7 +48,9 @@
                 '<data>',
                 '<',
                 request['class'],
-                ' xmlns="urn:opencord:params:xml:ns:voltha:ietf-voltha">',
+                ' xmlns="',
+                request['namespace'],
+                '">',
                 '<',
                 request['subclass'],
                 '>',
diff --git a/netconf/nc_server.py b/netconf/nc_server.py
index 6c43194..9c19794 100644
--- a/netconf/nc_server.py
+++ b/netconf/nc_server.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
 from session.nc_connection import NetconfConnection
 from session.session_mgr import get_session_manager_instance
 from constants import Constants as C
+from capabilities import Capabilities
 
 dir_path = os.path.dirname(os.path.realpath(__file__))
 
@@ -97,6 +98,7 @@
         self.client_passwords_file = client_passwords_file
         self.session_mgr = get_session_manager_instance()
         self.grpc_client = grpc_client
+        self.capabilities = Capabilities()
         self.connector = None
         self.nc_client_map = {}
         self.running = False
@@ -118,6 +120,14 @@
         self.d_stopped.callback(None)
         log.info('stopped')
 
+    def set_capabilities(self):
+        yang_schemas = self.grpc_client.yang_schemas
+        if not yang_schemas:
+            log.error('no-yang-schema')
+            return
+        self.capabilities.set_server_capabilities(yang_schemas)
+        self.capabilities.set_schema_dir(self.grpc_client.work_dir)
+
     def reload_capabilities(self):
         # TODO: Called when there is a reconnect to voltha
         # If there are new device types then the new
@@ -139,7 +149,8 @@
         #create a session
         session = self.session_mgr.create_session(client_conn.avatar.get_user())
         handler = NetconfProtocolHandler(self, client_conn,
-                                         session, self.grpc_client)
+                                         session, self.grpc_client,
+                                         self.capabilities)
         client_conn.proto_handler = handler
         reactor.callLater(0, handler.start)
 
@@ -176,6 +187,7 @@
     def start_ssh_server(self):
         try:
             log.debug('starting', port=self.netconf_port)
+            self.set_capabilities()
             self.portal = self.setup_secure_access()
             self.connector = reactor.listenTCP(self.netconf_port, self)
             log.debug('started', port=self.netconf_port)
diff --git a/netconf/protoc_plugins/descriptor_parser.py b/netconf/protoc_plugins/descriptor_parser.py
index c23f497..0034379 100644
--- a/netconf/protoc_plugins/descriptor_parser.py
+++ b/netconf/protoc_plugins/descriptor_parser.py
@@ -1,5 +1,5 @@
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/netconf/protoc_plugins/proto2yang.py b/netconf/protoc_plugins/proto2yang.py
index adc6338..95aaf99 100755
--- a/netconf/protoc_plugins/proto2yang.py
+++ b/netconf/protoc_plugins/proto2yang.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -782,7 +782,8 @@
 
 
 def remove_unsupported_characters(text):
-    unsupported_characters = ["{", "}", "[", "]", "\"", "\\", "*", "/"]
+    unsupported_characters = ["{", "}", "[", "]", "\"", "\\", "*", "/", "<",
+                              ">"]
     return ''.join([i if i not in unsupported_characters else ' ' for i in
                     text])
 
diff --git a/netconf/session/nc_connection.py b/netconf/session/nc_connection.py
index d8a2afe..9043584 100644
--- a/netconf/session/nc_connection.py
+++ b/netconf/session/nc_connection.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,8 +23,7 @@
 
 log = structlog.get_logger()
 
-from netconf import MAXSSHBUF
-
+MAXSSHBUF = C.MAXSSHBUF
 
 class NetconfConnection(protocol.Protocol):
     def __init__(self, data=None, avatar=None, max_chunk=MAXSSHBUF):
diff --git a/netconf/session/nc_protocol_handler.py b/netconf/session/nc_protocol_handler.py
index 64005c4..02d1454 100644
--- a/netconf/session/nc_protocol_handler.py
+++ b/netconf/session/nc_protocol_handler.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,28 +18,25 @@
 import io
 from lxml import etree
 from lxml.builder import E
-import netconf.nc_common.error as ncerror
-from netconf import NSMAP, qmap
-from utils import elm
 from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
-from capabilities import Capabilities
 from netconf.nc_rpc.rpc_factory import get_rpc_factory_instance
 from netconf.constants import Constants as C
+from netconf.nc_common.utils import qmap, ns, elm
+import netconf.nc_common.error as ncerror
 
 log = structlog.get_logger()
 
-
 class NetconfProtocolError(Exception): pass
 
 
 class NetconfProtocolHandler:
-    def __init__(self, nc_server, nc_conn, session, grpc_client):
+    def __init__(self, nc_server, nc_conn, session, grpc_client, capabilities):
         self.started = True
         self.conn = nc_conn
         self.nc_server = nc_server
         self.grpc_client = grpc_client
         self.new_framing = False
-        self.capabilities = Capabilities()
+        self.capabilities = capabilities
         self.session = session
         self.exiting = False
         self.connected = Deferred()
@@ -53,7 +50,7 @@
         return self.conn.receive_msg_any(self.new_framing)
 
     def send_hello(self, caplist, session=None):
-        msg = elm(C.HELLO, attrib={C.XMLNS: NSMAP[C.NC]})
+        msg = elm(C.HELLO, attrib={C.XMLNS: ns(C.NC)})
         caps = E.capabilities(*[E.capability(x) for x in caplist])
         msg.append(caps)
 
@@ -96,7 +93,7 @@
             # Parse reply
             tree = etree.parse(io.BytesIO(reply.encode('utf-8')))
             root = tree.getroot()
-            caps = root.xpath(C.CAPABILITY_XPATH, namespaces=NSMAP)
+            caps = root.xpath(C.CAPABILITY_XPATH, namespaces=C.NS_MAP)
 
             # Store capabilities
             for cap in caps:
@@ -152,7 +149,7 @@
                 self.close()
             return
 
-        rpcs = tree.xpath(C.RPC_XPATH, namespaces=NSMAP)
+        rpcs = tree.xpath(C.RPC_XPATH, namespaces=C.NS_MAP)
         if not rpcs:
             raise ncerror.SessionError(msg, "No rpc found")
 
@@ -172,7 +169,8 @@
                 rpc_handler = rpc_factory.get_rpc_handler(rpc,
                                                           msg,
                                                           self.grpc_client,
-                                                          self.session)
+                                                          self.session,
+                                                          self.capabilities)
                 if rpc_handler:
                     # set the parameters for this handler
                     response = yield rpc_handler.execute()
diff --git a/netconf/session/session.py b/netconf/session/session.py
index 51979f7..82157d7 100644
--- a/netconf/session/session.py
+++ b/netconf/session/session.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/netconf/session/session_mgr.py b/netconf/session/session_mgr.py
index ee4382f..ee8f355 100644
--- a/netconf/session/session_mgr.py
+++ b/netconf/session/session_mgr.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2016 the original author or authors.
+# Copyright 2017 the original author or authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/netconf/utils.py b/netconf/utils.py
deleted file mode 100644
index 455a395..0000000
--- a/netconf/utils.py
+++ /dev/null
@@ -1,299 +0,0 @@
-# -*- coding: utf-8 -*-#
-#
-# March 31 2015, Christian Hopps <chopps@gmail.com>
-#
-# Copyright (c) 2015, Deutsche Telekom AG
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-from __future__ import absolute_import, division, unicode_literals, \
-    print_function, nested_scopes
-from netconf import NSMAP
-import copy
-from lxml import etree
-
-
-# Tries to somewhat implement RFC6241 filtering
-
-
-def qname(tag):
-    try:
-        return etree.QName(tag)
-    except ValueError:
-        prefix, base = tag.split(":")
-        return etree.QName(NSMAP[prefix], base)
-
-
-def elm(tag, attrib=None, **extra):
-    if attrib is None:
-        attrib = dict()
-    return etree.Element(qname(tag), attrib, **extra)
-
-
-def leaf_elm(tag, value, attrib=None, **extra):
-    e = elm(tag, attrib, **extra)
-    e.text = str(value)
-    return e
-
-
-leaf = leaf_elm
-
-
-def subelm(pelm, tag, attrib=None, **extra):
-    if attrib is None:
-        attrib = dict()
-    return etree.SubElement(pelm, qname(tag), attrib, **extra)
-
-
-def is_selection_node(felm):
-    ftext = felm.text
-    return ftext is None or not ftext.strip()
-
-
-def filter_tag_match(filter_tag, elm_tag):
-    fqname = etree.QName(filter_tag)
-    eqname = qname(elm_tag)
-    if not fqname.namespace:
-        return fqname.localname == eqname.localname
-    return fqname == eqname
-
-
-def filter_node_match_no_value(filter_node, match_elm):
-    # First check to see if tag matches.
-    if not filter_tag_match(filter_node.tag, match_elm.tag):
-        return False
-
-    # Next check for attribute matches.
-    # XXX does this need to filter out namespace attributes?
-    if filter_node.attrib and filter_node.attrib != match_elm.attrib:
-        return False
-
-    return True
-
-
-def filter_node_match(filter_node, match_elm):
-    """Given a filter node element and a nodename and attribute dictionary
-    return true if the filter element matches the elmname, attributes and value
-    (if not None).
-
-    The filter element can use a wildcard namespace or a specific namespace
-    the attributes can be missing from the filter node but otherwise must match
-    and the value is only checked for a match if it is not None.
-    """
-    if not filter_node_match_no_value(filter_node, match_elm):
-        return False
-
-    # Finally check for matching value.
-    ftext = filter_node.text
-    if ftext is None:
-        return True
-
-    ftext = ftext.strip()
-    if not ftext:
-        return True
-
-    return ftext == match_elm.text
-
-
-def filter_leaf_values(fcontain_elm, dest_node, leaf_elms, append_to):
-    """Given a containment element (or None) verify that all leaf elements
-    in leaf_elms either match, have corresponding selection nodes (empty)
-    or are not present.
-
-    Additionally the correct leaf data will be added to dest_node, and dest_node
-    will be appended to append_to if append_to is not None.
-
-    The return value with be True, False, or a possibly empty set of selection/containment nodes
-    The only failing value is False, if True is returned then the caller should include all
-    containment sibling nodes, otherwise the caller should process the list of containment/selection
-    nodes.
-    """
-    children = fcontain_elm.getchildren() if fcontain_elm is not None else []
-    selected_elms = []
-    if not children:
-        selected_elms = leaf_elms
-
-    # Now look at all the leaf filter selector or match nodes
-    include_all_leaves = True
-    othernodes = []
-    for felm in children:
-        fchildren = felm.getchildren()
-        for lelm in leaf_elms:
-            if fchildren:
-                # Verify that this doesn't match a leaf node.
-                if filter_node_match_no_value(felm, lelm):
-                    # XXX this is an error we should raise some exception.
-                    return False
-                continue
-            elif filter_node_match(felm, lelm):
-                if not felm.text:
-                    # This was a selection node.
-                    include_all_leaves = False
-
-                selected_elms.append(lelm)
-                break
-        else:
-            if fchildren:
-                # This is OK we verified a containment filter didn't match leaf by getting here.
-                if felm.text:
-                    # XXX verify that there is no text on this node, report violation?
-                    return False
-
-                # Track selection/filter nodes
-                include_all_leaves = False
-                othernodes.append(felm)
-            elif not felm.text:
-                # This is OK as it means this is a selection node include it in othernodes
-                include_all_leaves = False
-                othernodes.append(felm)
-            else:
-                # We've exhausted all leaf elements to match this leaf filter so we failed.
-                return False
-
-    # Everything matched so add in the leaf data.
-    if append_to is not None:
-        append_to.append(dest_node)
-
-    if include_all_leaves:
-        dest_node.extend(leaf_elms)
-    else:
-        dest_node.extend(selected_elms)
-
-    if include_all_leaves:
-        return True
-    return othernodes
-
-
-def filter_containment_iter(fcontain_elm, dest_node, containment_nodes,
-                            leaf_elms, append_to):
-    """Given a containment filter node (or None) verify that all leaf elements
-    either match, have corresponding selection nodes (empty) or are not present.
-
-    If all leaf criteria are met then the iterator will return a triple of
-    (new_filter_node, new_dest_node, new_data). new_filter_node corresponds to the
-    matched containment node which is returned in new_dest_node, and new_data will be
-    an element corresponding to the passed in dest_node.
-
-    These should be processed by calling filter_containment_iter again.
-
-    Additionally the correct leaf data will be added to dest_node, and dest_node
-    will be appended to append_to if append_to is not None.
-
-    This implements RFC6241 section 6.2.5
-    """
-    # No containment node so add everything.
-    if fcontain_elm is None:
-        # Add in the leaf data
-        for e in leaf_elms:
-            dest_node.append(e)
-
-        # Append the match_node to the data
-        if append_to is not None:
-            append_to.append(dest_node)
-
-        for node in containment_nodes:
-            yield None, copy.copy(node), dest_node
-
-    else:
-        othernodes = filter_leaf_values(fcontain_elm, dest_node, leaf_elms,
-                                        append_to)
-        if othernodes is False:
-            # No match
-            pass
-        elif othernodes is True:
-            # All leaf values have matched and have been added and we should include all containers
-            for node in containment_nodes:
-                yield None, copy.copy(node), dest_node
-        else:
-            for felm in othernodes:
-                for node in containment_nodes:
-                    if filter_node_match_no_value(felm, node):
-                        yield felm, copy.copy(node), dest_node
-
-
-def filter_leaf_allows_add(filter_elm, tag, data, value):
-    if filter_leaf_allows(filter_elm, tag, value):
-        data.append(leaf_elm(tag, value))
-        return True
-    return False
-
-
-def filter_leaf_allows(filter_elm, xpath, value):
-    """Check the value at the xpath specified leaf matches the value.
-
-    If filter_elm is None then allow.
-    If there is no xpath element then allow if there are no other children.
-    XXX what about xpath that has embedded predicates!
-        perhaps what we want to call this is a normal path not an xpath.
-    """
-    if filter_elm is None:
-        return True
-
-    # If there are no children then allow everything.
-    if not filter_elm.getchildren():
-        return True
-
-    # No match or multiple matches not allowed for leaf.
-    flist = filter_elm.xpath(xpath, namespaces=NSMAP)
-    if not flist or len(flist) > 1:
-        return False
-    felm = flist[0]
-
-    # No children for leaf allowed (leaf)
-    if felm.getchildren():
-        return False
-
-    # Allowed if empty or if value matches.
-    if not felm.text or felm.text == str(value):
-        return True
-
-    return False
-
-
-def filter_list_iter(filter_list, key_xpath, keys):
-    """Return key, elm pairs that are allowed by keys using the values found using the given key_xpath"""
-    # If we have no filter elm then return all keys.
-    if filter_list is None:
-        for key in keys:
-            yield key, None
-
-    try:
-        # If this an element then make it a list of elements
-        filter_list.xpath  # pylint: disable=W0104
-        filter_list = [filter_list]
-    except AttributeError:
-        pass
-
-    for filter_elm in filter_list:
-        filter_elms = [x for x in
-                       filter_elm.xpath(key_xpath, namespaces=NSMAP)]
-        filter_keys = [x.text for x in filter_elms]
-        if not filter_keys:
-            for key in keys:
-                yield key, filter_elm
-        else:
-            # Now walk our keys returning any that are in the filter list.
-            for key in keys:
-                if key in filter_keys:
-                    yield key, filter_elm
-                    # try:
-                    #     idx = filter_keys.index(str(key))
-                    #     yield key, filter_elm
-                    # except ValueError:
-                    #     pass
-
-
-__author__ = 'Christian Hopps'
-__date__ = 'March 31 2015'
-__version__ = '1.0'
-__docformat__ = "restructuredtext en"