This commit consists of:
1) Improved error handling
2) Return correct XML message for unimplemented rpcs
3) Clean up

Change-Id: Ia59d203840efc2e238a50d4f05e56f854cca9fc7
diff --git a/netconf/nc_common/error.py b/netconf/nc_common/error.py
index bb28490..0cf900d 100644
--- a/netconf/nc_common/error.py
+++ b/netconf/nc_common/error.py
@@ -72,19 +72,6 @@
     XML_ERROR_SEV = "error-severity"
 
 
-    # def __init__(self, replynode=None, error_type=None, error_tag=None,
-    #              error_severity=None, error_tag_app=None, error_path=None,
-    #              error_message=None):
-    #
-    #     self.replynode = replynode
-    #     self.error_type = error_type
-    #     self.error_tag = error_tag
-    #     self.error_severity = error_severity
-    #     self.error_tag_app = error_tag_app
-    #     self.error_path = error_path
-    #     self.error_message = error_message
-    #     self.error_info = []
-
 class ChannelClosed(Error):
     pass
 
@@ -117,23 +104,23 @@
         if "severity" not in kwargs:
             etree.SubElement(rpcerr, Error.XML_ERROR_SEV).text = "error"
 
-        # Convert all remaining arguments to xml
-        for key, value in kwargs.items():
-            key = key.replace('_', '-')
-            etree.SubElement(rpcerr, "error-{}".format(key)).text = str(value)
+        # # Convert all remaining arguments to xml
+        # for key, value in kwargs.items():
+        #     key = key.replace('_', '-')
+        #     etree.SubElement(rpcerr, "error-{}".format(key)).text = str(value)
 
-        # super(RPCError, self).__init__(self.get_xml_reply())
-
-    def get_xml_reply(self):
-        return etree.tounicode(self.reply)
 
 
 class BadMsg(RPCError):
-    def __init__(self, origmsg):
-        RPCError.__init__(self,
-                          origmsg,
-                          Error.RPC,
-                          Error.MALFORMED_MESSAGE)
+    def __init__(self, origmsg, exception=None):
+        super(BadMsg, self).__init__(
+                            origmsg,
+                            Error.RPC,
+                            Error.MALFORMED_MESSAGE,
+                            info=str(exception))
+
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
 
 
 class InvalidValue(Error):
@@ -143,6 +130,9 @@
                           Error.RPC,
                           Error.INVALID_VALUE,
                           **kwargs)
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
+
 
 
 class MissingElement(RPCError):
@@ -153,6 +143,9 @@
                           Error.MISSING_ELEMENT,
                           info=tag,
                           **kwargs)
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
+
 
 
 class BadElement(RPCError):
@@ -164,6 +157,9 @@
                           info=element.tag,
                           **kwargs)
 
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
+
 
 class UnknownElement(RPCError):
     def __init__(self, origmsg, element, **kwargs):
@@ -173,23 +169,31 @@
                           Error.UNKNOWN_ELEMENT,
                           info=element.tag,
                           **kwargs)
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
 
 
 class NotImpl(RPCError):
     def __init__(self, origmsg, **kwargs):
         RPCError.__init__(self,
                           origmsg,
-                          Error.PROTOCOL,
+                          Error.APPLICATION,
                           Error.OPERATION_NOT_SUPPORTED,
                           **kwargs)
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
+
 
 
 class ServerException(RPCError):
     def __init__(self, origmsg, exception, **kwargs):
         RPCError.__init__(self,
                           origmsg,
-                          Error.PROTOCOL,
+                          Error.APPLICATION,
                           Error.OPERATION_FAILED,
                           info=str(exception),
                           **kwargs)
 
+    def get_xml_reply(self):
+        return etree.tounicode(self.reply)
+
diff --git a/netconf/nc_rpc/base/get.py b/netconf/nc_rpc/base/get.py
index d953b17..b2bb9fe 100644
--- a/netconf/nc_rpc/base/get.py
+++ b/netconf/nc_rpc/base/get.py
@@ -40,8 +40,8 @@
         if not rpc:
             log.info('unsupported-request', request=self.request)
             self.rpc_response.is_error = True
-            self.rpc_response.node = ncerror.BadMsg(self.request)
-            return
+            self.rpc_response.node = ncerror.NotImpl(self.request_xml)
+            returnValue(self.rpc_response)
 
         # Invoke voltha via the grpc client
         res_dict = yield self.grpc_client.invoke_voltha_api(rpc)
@@ -66,18 +66,18 @@
             try:
                 if self.request['command'] != 'get':
                     self.rpc_response.is_error = True
-                    self.rpc_response.node = ncerror.BadMsg('No GET in get '
-                                                            'request')
+                    self.rpc_response.node = ncerror.BadMsg(self.request_xml)
+                    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')
+                        self.rpc_response.node = ncerror.NotImpl(self.request_xml)
+                    return
 
             except Exception as e:
                 self.rpc_response.is_error = True
-                self.rpc_response.node = ncerror.BadMsg(self.request)
+                self.rpc_response.node = ncerror.ServerException(self.request_xml)
                 return
 
     def get_voltha_rpc(self, request):
@@ -92,6 +92,11 @@
                         'subclass']:
                         return rpc['rpc']
 
+            # If the request has a subclass and is not in the list of
+            # supported rpc then return None
+            if request.has_key('subclass'):
+                return None
+
             # If we are here then no subclass exists.  Just return the rpc
             # associated with theNone subclass
             for rpc in rpcs:
@@ -128,9 +133,9 @@
             {'subclass': 'device_groups',
              'rpc': 'VolthaLocalService-ListDeviceGroups'
              },
-        ],
-        'VolthaInstances': [
-            {'subclass': None,
-             'rpc': 'VolthaGlobalService-ListVolthaInstances'
-             }],
+        ]
+        # 'VolthaInstances': [
+        #     {'subclass': None,
+        #      'rpc': 'VolthaGlobalService-ListVolthaInstances'
+        #      }],
     }
diff --git a/netconf/nc_rpc/ext/get_schema.py b/netconf/nc_rpc/ext/get_schema.py
index ec473d3..569a3b0 100644
--- a/netconf/nc_rpc/ext/get_schema.py
+++ b/netconf/nc_rpc/ext/get_schema.py
@@ -19,7 +19,8 @@
 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
+from netconf.nc_common.utils import qmap
+from twisted.internet.defer import inlineCallbacks, returnValue
 
 log = structlog.get_logger()
 
@@ -31,26 +32,28 @@
         self.parse_schema_request(request_xml)
         self._validate_parameters()
 
+    @inlineCallbacks
     def execute(self):
         if self.rpc_response.is_error:
-            return(self.rpc_response)
+            returnValue(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'])
+        content = yield 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 = ncerror.BadMsg(self.request_xml)
+            returnValue(self.rpc_response)
 
-        self.rpc_response.node = self.create_xml_response(content)
+        self.rpc_response.node = yield self.create_xml_response(content)
 
         self.rpc_response.is_error = False
 
-        return(self.rpc_response)
+        returnValue(self.rpc_response)
 
     def _validate_parameters(self):
         log.info('validate-parameters', session=self.session.session_id)
@@ -62,16 +65,14 @@
                         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')
+                    self.rpc_response.node = ncerror.BadMsg(self.request_xml)
                     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')
+                            self.request_xml)
                         return
 
                 # Verify that the requested schema exists
@@ -79,8 +80,7 @@
                                                              'identifier']) \
                         or self.request['format'] != 'yang' :
                     self.rpc_response.is_error = True
-                    self.rpc_response.node = ncerror.BadMsg(
-                        'Unsupported request')
+                    self.rpc_response.node = ncerror.BadMsg(self.request_xml)
                     return
 
             except Exception as e:
diff --git a/netconf/nc_rpc/ext/get_schemas.py b/netconf/nc_rpc/ext/get_schemas.py
index 7ec4555..cc09ebb 100644
--- a/netconf/nc_rpc/ext/get_schemas.py
+++ b/netconf/nc_rpc/ext/get_schemas.py
@@ -18,6 +18,8 @@
 import structlog
 from netconf.nc_rpc.rpc import Rpc
 import netconf.nc_common.error as ncerror
+from twisted.internet.defer import inlineCallbacks, returnValue
+
 
 log = structlog.get_logger()
 
@@ -28,9 +30,10 @@
         self._validate_parameters()
         self.capabilities = capabilities
 
+    @inlineCallbacks
     def execute(self):
         if self.rpc_response.is_error:
-            return(self.rpc_response)
+            returnValue(self.rpc_response)
 
         log.info('get-schemas-request', session=self.session.session_id,
                  request=self.request)
@@ -55,12 +58,12 @@
             node.text = dict['location']
 
         # Build the yang response
-        self.rpc_response.node = self.rpc_response.build_xml_response(
+        self.rpc_response.node = yield self.rpc_response.build_xml_response(
             self.request, top)
 
         self.rpc_response.is_error = False
 
-        return(self.rpc_response)
+        returnValue(self.rpc_response)
 
 
     def _validate_parameters(self):
@@ -70,15 +73,14 @@
             try:
                 if self.request['command'] != 'get-schemas':
                     self.rpc_response.is_error = True
-                    self.rpc_response.node = ncerror.BadMsg('Improperly '
-                                                            'formatted get '
-                                                            'schemas request')
+                    self.rpc_response.node = ncerror.BadMsg(self.request_xml)
+                    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')
+                        self.rpc_response.node = ncerror.BadMsg(self.request_xml)
+                        return
 
             except Exception as e:
                 self.rpc_response.is_error = True
diff --git a/netconf/nc_rpc/rpc_factory.py b/netconf/nc_rpc/rpc_factory.py
index 3d0d678..f314dbe 100644
--- a/netconf/nc_rpc/rpc_factory.py
+++ b/netconf/nc_rpc/rpc_factory.py
@@ -42,13 +42,7 @@
     instance = None
 
     def __init__(self):
-        self.rpc_map = {}
-        # TODO:  This will be loaded after the yang modules have been
-        # generated from proto files
-        self.register_rpc('{urn:opencord:params:xml:ns:voltha:ietf-voltha}',
-                          'VolthaGlobalService', 'GetVoltha', GetVoltha)
-        self.register_rpc('{urn:opencord:params:xml:ns:voltha:ietf-voltha}',
-                          'any', 'any', GetVoltha)
+        pass
 
     def _get_key(self, namespace, service, name):
         return ''.join([namespace, service, name])
@@ -60,8 +54,8 @@
 
 
     # 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
+    # TODO:  This parser is specific for a GET/GET SCHEMAS request.  Need to be
+    #  it more generic
     def parse_xml_request(self, node):
         request = {}
         if not len(node):
@@ -99,15 +93,6 @@
 
         return request
 
-    def register_rpc(self, namespace, service, name, klass):
-        key = self._get_key(namespace, service, name)
-        if key not in self.rpc_map.keys():
-            self.rpc_map[key] = klass
-
-    def get_handler(self, namespace, service, name):
-        key = self._get_key(namespace, service, name)
-        if key in self.rpc_map.keys():
-            return self.rpc_map[key]
 
     def get_rpc_handler(self, rpc_node, msg, grpc_channel, session,
                         capabilities):
@@ -135,9 +120,13 @@
 
             log.error("rpc-not-implemented", rpc=request['command'])
 
-        except Exception as e:
+        except ncerror.BadMsg as err:
+            log.info('ncerror.BadMsg')
             raise ncerror.BadMsg(rpc_node)
 
+        except Exception as e:
+            raise ncerror.ServerException(rpc_node, exception=e)
+
     rpc_class_handlers = {
         'getvoltha': GetVoltha,
         'get-config': GetConfig,
diff --git a/netconf/nc_rpc/rpc_response.py b/netconf/nc_rpc/rpc_response.py
index 150631b..a544598 100644
--- a/netconf/nc_rpc/rpc_response.py
+++ b/netconf/nc_rpc/rpc_response.py
@@ -41,6 +41,9 @@
             voltha_xml_string = voltha_xml_string[len('<yang>'):]
             if voltha_xml_string.endswith('</yang>'):
                 voltha_xml_string = voltha_xml_string[:-len('</yang>')]
+        # Empty response
+        elif voltha_xml_string.startswith('<yang/>'):
+            voltha_xml_string=''
 
         # Create the xml body as
         if request.has_key('subclass'):
@@ -151,6 +154,7 @@
         # special case the xml contain a list type
         if len(elms) == 1:
             item = elms[0]
+            #TODO: Address the case where the content is a list of list
             if item.get('type') == 'list':
                 item.tag = 'ignore'
                 self.add_node(self.process_element(item), top)
diff --git a/netconf/session/nc_protocol_handler.py b/netconf/session/nc_protocol_handler.py
index 02d1454..79cfb06 100644
--- a/netconf/session/nc_protocol_handler.py
+++ b/netconf/session/nc_protocol_handler.py
@@ -23,6 +23,7 @@
 from netconf.constants import Constants as C
 from netconf.nc_common.utils import qmap, ns, elm
 import netconf.nc_common.error as ncerror
+from netconf.nc_common.error import BadMsg, NotImpl, ServerException, Error
 
 log = structlog.get_logger()
 
@@ -178,8 +179,10 @@
                              rpc_handler=rpc_handler,
                              is_error=response.is_error,
                              response=response)
-                    self.send_rpc_reply(response.node, rpc)
-                    # self.send_rpc_reply(self.get_instance(), rpc)
+                    if not response.is_error:
+                        self.send_rpc_reply(response.node, rpc)
+                    else:
+                        self.send_message(response.node.get_xml_reply())
 
                     if response.close_session:
                         log.info('response-closing-session', response=response)
@@ -188,12 +191,13 @@
                     log.error('no-rpc-handler',
                               request=msg,
                               session_id=self.session.session_id)
-                    raise ncerror.NotImpl(msg)
+                    error = ncerror.NotImpl(msg)
+                    self.send_message(error.get_xml_reply())
 
             except ncerror.BadMsg as err:
                 log.info('ncerror.BadMsg')
                 if self.new_framing:
-                    self.send_message(err.get_reply_msg())
+                    self.send_message(err.get_xml_reply())
                 else:
                     # If we are 1.0 we have to simply close the connection
                     # as we are not allowed to send this error
@@ -205,7 +209,7 @@
             except Exception as ex:
                 log.info('Exception', repr(ex))
                 error = ncerror.ServerException(rpc, ex)
-                self.send_message(error.get_reply_msg())
+                self.send_message(error.get_xml_reply())
 
 
     def stop(self, reason):