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):