This is the initial commit of the netconf server code. It consists
of the following:
1) The server is built using Twisted Conch
2) It adapted an existing opensource netconf server (https://github.com/choppsv1/netconf)
to handle some low-level protocols. The adaptation is mostly around
using Twisted Conch instead of Python Threads
3) A microservice to interface with Voltha on the SB and Netconf client on
the NB
4) A set of credentials for the server and clients. At this time these
credentials are local and in files. Additional work is required to
secure these files
5) A rough-in to handle the rpc requests from Netconf clients
6) Code for initial handshaking is in place (hello)
Change-Id: I1ca0505d0ac35ff06066b107019ae87ae30e38f8
diff --git a/netconf/error.py b/netconf/error.py
new file mode 100644
index 0000000..7a33f69
--- /dev/null
+++ b/netconf/error.py
@@ -0,0 +1,193 @@
+# -*- coding: utf-8 -*-#
+#
+# February 19 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 lxml import etree
+from netconf import NSMAP
+
+
+class NetconfException(Exception):
+ pass
+
+
+class ChannelClosed(NetconfException):
+ pass
+
+
+class FramingError(NetconfException):
+ pass
+
+
+class SessionError(NetconfException):
+ pass
+
+
+class RPCError(NetconfException):
+ def __init__(self, output, tree, error):
+ super(RPCError, self).__init__(output)
+ self.tree = tree
+ self.error = error
+
+ def _get_error_val(self, value):
+ try:
+ return self.error.xpath("nc:" + value, namespaces=NSMAP)[0].text
+ except IndexError:
+ return None
+
+ def get_error_tag(self):
+ return self._get_error_val("error-tag")
+
+ def get_error_type(self):
+ return self._get_error_val("error-type")
+
+ def get_error_info(self):
+ return self._get_error_val("error-info")
+
+ def get_error_severity(self):
+ return self._get_error_val("error-severity")
+
+
+# RFC6241
+
+# error-type
+RPCERR_TYPE_TRANSPORT = 0
+RPCERR_TYPE_RPC = 1
+RPCERR_TYPE_PROTOCOL = 2
+RPCERR_TYPE_APPLICATION = 3
+RPCERR_TYPE_ENUM = {
+ RPCERR_TYPE_TRANSPORT: "transport",
+ RPCERR_TYPE_RPC: "rpc",
+ RPCERR_TYPE_PROTOCOL: "protocol",
+ RPCERR_TYPE_APPLICATION: "application"
+}
+
+# error-tag
+RPCERR_TAG_IN_USE = "in-use"
+RPCERR_TAG_INVALID_VALUE = "invalid-value"
+RPCERR_TAG_TOO_BIG = "too-big"
+RPCERR_TAG_MISSING_ATTRIBUTE = "missing-attribute"
+RPCERR_TAG_BAD_ATTRIBUTE = "bad-attribute"
+RPCERR_TAG_UNKNOWN_ATTRIBUTE = "unknown-attribute"
+RPCERR_TAG_MISSING_ELEMENT = "missing-element"
+RPCERR_TAG_BAD_ELEMENT = "bad-element"
+RPCERR_TAG_UNKNOWN_ELEMENT = "unknown-element"
+RPCERR_TAG_UNKNOWN_NAMESPACE = "unknown-namespace"
+RPCERR_TAG_ACCESS_DENIED = "access-denied"
+RPCERR_TAG_LOCK_DENIED = "lock-denied"
+RPCERR_TAG_RESOURCE_DENIED = "resource-denied"
+RPCERR_TAG_ROLLBACK_FAILED = "rollback-failed"
+RPCERR_TAG_DATA_EXISTS = "data-exists"
+RPCERR_TAG_DATA_MISSING = "data-missing"
+RPCERR_TAG_OPERATION_NOT_SUPPORTED = "operation-not-supported"
+RPCERR_TAG_OPERATION_FAILED = "operation-failed"
+RPCERR_TAG_MALFORMED_MESSAGE = "malformed-message"
+
+
+# error-app-tag
+# error-path # xpath associated with error.
+# error-message # human readable message describiing error
+# error-info
+
+
+class RPCServerError(NetconfException):
+ def __init__(self, origmsg, etype, tag, **kwargs):
+ # Add attrib and nsmap from original message.
+ self.reply = etree.Element("rpc-reply", attrib=origmsg.attrib,
+ nsmap=origmsg.nsmap)
+
+ rpcerr = etree.SubElement(self.reply, "rpc-error")
+
+ # We require a type, tag, and severity assuming error for severity.
+ if etype in RPCERR_TYPE_ENUM:
+ etype = RPCERR_TYPE_ENUM[etype]
+ etree.SubElement(rpcerr, "error-type").text = str(etype)
+
+ etree.SubElement(rpcerr, "error-tag").text = tag
+
+ if "severity" not in kwargs:
+ etree.SubElement(rpcerr, "error-severity").text = "error"
+
+ # Now convert any other arguments to xml
+ for key, value in kwargs.items():
+ key = key.replace('_', '-')
+ etree.SubElement(rpcerr, "error-{}".format(key)).text = str(value)
+
+ # This sort of sucks for humans
+ super(RPCServerError, self).__init__(self.get_reply_msg())
+
+ def get_reply_msg(self):
+ return etree.tounicode(self.reply)
+
+
+class RPCSvrErrBadMsg(RPCServerError):
+ """If the server raises this exception the and netconf 1.0 is in use, the session will be closed"""
+
+ def __init__(self, origmsg):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_RPC,
+ RPCERR_TAG_MALFORMED_MESSAGE)
+
+
+class RPCSvrInvalidValue(RPCServerError):
+ def __init__(self, origmsg, **kwargs):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_RPC,
+ RPCERR_TAG_INVALID_VALUE, **kwargs)
+
+
+class RPCSvrMissingElement(RPCServerError):
+ def __init__(self, origmsg, tag, **kwargs):
+ try:
+ # Old API had this as an element...
+ tag = tag.tag
+ except AttributeError:
+ pass
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_RPC,
+ RPCERR_TAG_MISSING_ELEMENT, info=tag, **kwargs)
+
+
+class RPCSvrBadElement(RPCServerError):
+ def __init__(self, origmsg, element, **kwargs):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_RPC,
+ RPCERR_TAG_BAD_ELEMENT, info=element.tag,
+ **kwargs)
+
+
+class RPCSvrUnknownElement(RPCServerError):
+ def __init__(self, origmsg, element, **kwargs):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_RPC,
+ RPCERR_TAG_UNKNOWN_ELEMENT, info=element.tag,
+ **kwargs)
+
+
+class RPCSvrErrNotImpl(RPCServerError):
+ def __init__(self, origmsg, **kwargs):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_PROTOCOL,
+ RPCERR_TAG_OPERATION_NOT_SUPPORTED, **kwargs)
+
+
+class RPCSvrException(RPCServerError):
+ def __init__(self, origmsg, exception, **kwargs):
+ RPCServerError.__init__(self, origmsg, RPCERR_TYPE_PROTOCOL,
+ RPCERR_TAG_OPERATION_FAILED,
+ info=str(exception), **kwargs)
+
+
+__author__ = 'Christian Hopps'
+__date__ = 'February 19 2015'
+__version__ = '1.0'
+__docformat__ = "restructuredtext en"