This commit consists of:
1) Parsing protobuf responses from Voltha into a dict which will also
   include the yang proto annotations
2) Converting a protobuf response into a yang-compatible XML format
3) Support for GET (no request params) for Voltha, VolthaInstance and VolthaInstances
4) Minor bug fixes
5) Testing done using the MG-Soft Netconf client

Change-Id: Ibb7f62a391e19b0240cc739919fccc689a316005
diff --git a/netconf/nc_rpc/rpc_factory.py b/netconf/nc_rpc/rpc_factory.py
index aa0ced1..230c57a 100644
--- a/netconf/nc_rpc/rpc_factory.py
+++ b/netconf/nc_rpc/rpc_factory.py
@@ -35,6 +35,11 @@
 log = structlog.get_logger()
 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}'
+}
+
 
 class RpcFactory:
     instance = None
@@ -51,6 +56,39 @@
     def _get_key(self, namespace, service, name):
         return ''.join([namespace, service, name])
 
+    def get_attribute_value(self, name, attributes):
+        for tup in attributes.items():
+            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
+    def parse_xml_request(self, node):
+        request = {}
+        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_name == 'rpc':
+                    request['type'] = 'rpc'
+                    request['message_id'] = self.get_attribute_value(
+                        'message-id', elem.attrib)
+                elif elem_name == 'filter':
+                    request['filter'] = self.get_attribute_value('type',
+                                                                 elem.attrib)
+                else:
+                    request[
+                        'command'] = elem_name  # attribute is empty for now
+            elif elem.tag.find(ns_map['voltha']) != -1:  # found
+                if request.has_key('class'):
+                    request['subclass'] = elem.tag.replace(ns_map['voltha'],
+                                                           "")
+                else:
+                    request['class'] = elem.tag.replace(ns_map['voltha'], "")
+        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():
@@ -63,33 +101,32 @@
 
     def get_rpc_handler(self, rpc_node, msg, grpc_channel, session):
         try:
-            msg_id = rpc_node.get('message-id')
-            log.info("Received-rpc-message-id", msg_id=msg_id)
+            # Parse the request into a dictionary
+            log.info("rpc-node",
+                     node=etree.tostring(rpc_node, pretty_print=True))
 
-        except (TypeError, ValueError):
-            raise ncerror.SessionError(msg,
-                                       "No valid message-id attribute found")
+            request = self.parse_xml_request(rpc_node)
+            if not request:
+                log.error("request-bad-format")
+                raise ncerror.BadMsg(rpc_node)
 
-        log.info("rpc-node", node=etree.tostring(rpc_node, pretty_print=True))
+            if not request.has_key('message_id') or \
+                    not request.has_key('command'):
+                log.error("request-no-message-id")
+                raise ncerror.BadMsg(rpc_node)
 
-        # Get the first child of rpc as the method name
-        rpc_method = rpc_node.getchildren()
-        if len(rpc_method) != 1:
-            log.error("badly-formatted-rpc-method", msg_id=msg_id)
+            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)
+
+            log.error("rpc-not-implemented", rpc=request['command'])
+
+        except Exception as e:
             raise ncerror.BadMsg(rpc_node)
 
-        rpc_method = rpc_method[0]
-
-        rpc_name = rpc_method.tag.replace(qmap('nc'), "")
-
-        log.info("rpc-request", rpc=rpc_name)
-        class_handler = self.rpc_class_handlers.get(rpc_name, None)
-        if class_handler is not None:
-            return class_handler(rpc_node, rpc_method, None, grpc_channel,
-                                 session)
-
-        log.error("rpc-not-implemented", rpc=rpc_name)
-
     rpc_class_handlers = {
         'getvoltha': GetVoltha,
         'get-config': GetConfig,
@@ -109,13 +146,3 @@
     if RpcFactory.instance == None:
         RpcFactory.instance = RpcFactory()
     return RpcFactory.instance
-
-
-if __name__ == '__main__':
-    fac = get_rpc_factory_instance()
-    fac.register_rpc('urn:opencord:params:xml:ns:voltha:ietf-voltha',
-                     'VolthaGlobalService', 'GetVoltha', GetVoltha)
-    rpc = fac.get_handler('urn:opencord:params:xml:ns:voltha:ietf-voltha',
-                          'VolthaGlobalService', 'GetVoltha')
-    # rpc = fac.rpc_class_handlers.get('getvoltha', None)
-    print rpc(None, None, None, None)