def filter_results(rpc, data, filter_or_none, debug=False): """Check for a user filter and prune the result data accordingly. :param rpc: An RPC message element. :param data: The data to filter. :param filter_or_none: Filter element or None. :type filter_or_none: `lxml.Element` """ if filter_or_none is None: return data if 'type' not in filter_or_none.attrib or filter_or_none.attrib[ 'type'] == "subtree": # Check for the pathalogical case of empty filter since that's easy to implement. if not filter_or_none.getchildren(): return elm("data") # xpf = Convert subtree filter to xpath! logger.warning("Filtering with subtree not implemented yet.") raise error.OperationNotSupportedProtoError(rpc) elif filter_or_none.attrib['type'] == "xpath": if 'select' not in filter_or_none.attrib: raise error.MissingAttributeProtoError(rpc, filter_or_none, "select") xpf = filter_or_none.attrib['select'] else: msg = "unexpected type: " + str(filter_or_none.attrib['type']) raise error.BadAttributeProtoError(rpc, filter_or_none, "type", message=msg) logger.debug("Filtering on xpath expression: %s", str(xpf)) return xpath_filter_result(data, xpf)
def rpc_get(self, session, rpc, filter_or_none): # pylint: disable=W0613 """Passed the filter element or None if not present :param session: The server session with the client. :type session: `NetconfServerSession` :param rpc: The topmost element in the received message. :type rpc: `lxml.Element` :param filter_or_none: The filter element if present. :type filter_or_none: `lxml.Element` or None :return: `lxml.Element` of "nc:data" type containing the requested state. :raises: `error.RPCServerError` which will be used to construct an XML error response. """ raise ncerror.OperationNotSupportedProtoError(rpc)
def rpc_get_config(self, session, rpc, source_elm, filter_or_none): # pylint: disable=W0613 """The client has requested the config state (config: true). The function is passed the source element and the filter element or None if not present :param session: The server session with the client. :type session: `NetconfServerSession` :param rpc: The topmost element in the received message. :type rpc: `lxml.Element` :param source_elm: The source element indicating where the config should be drawn from. :type source_elm: `lxml.Element` :param filter_or_none: The filter element if present. :type filter_or_none: `lxml.Element` or None :return: `lxml.Element` of "nc:data" type containing the requested state. :raises: `error.RPCServerError` which will be used to construct an XML error response. """ raise ncerror.OperationNotSupportedProtoError(rpc)
def rpc_edit_config(self, unused_session, rpc, *unused_params): """XXX API subject to change -- unfinished""" raise ncerror.OperationNotSupportedProtoError(rpc)
def _rpc_not_implemented(self, unused_session, rpc, *unused_params): if self.debug: msg_id = rpc.get('message-id') logger.debug("%s: Not Impl msg-id: %s", str(self), msg_id) raise ncerror.OperationNotSupportedProtoError(rpc)
def rpc_unlock(self, unused_session, rpc, *unused_params): raise ncerror.OperationNotSupportedProtoError(rpc)
def rpc_edit_config(self, unused_session, rpc, *unused_params): raise ncerror.OperationNotSupportedProtoError(rpc)
def _reader_handle_message(self, msg): if not self.session_open: return # Any error with XML encoding here is going to cause a session close # Technically we should be able to return malformed message I think. try: tree = etree.parse(io.BytesIO(msg.encode('utf-8'))) if not tree: raise ncerror.SessionError(msg, "Invalid XML from client.") except etree.XMLSyntaxError: logger.warning("Closing session due to malformed message") raise ncerror.SessionError(msg, "Invalid XML from client.") rpcs = tree.xpath("/nc:rpc", namespaces=NSMAP) if not rpcs: raise ncerror.SessionError(msg, "No rpc found") for rpc in rpcs: try: msg_id = rpc.get('message-id') if self.debug: logger.debug("%s: Received rpc message-id: %s", str(self), msg_id) except (TypeError, ValueError): raise ncerror.SessionError( msg, "No valid message-id attribute found") try: # Get the first child of rpc as the method name rpc_method = rpc.getchildren() if len(rpc_method) != 1: if self.debug: logger.debug("%s: Bad Msg: msg-id: %s", str(self), msg_id) raise ncerror.MalformedMessageRPCError(rpc) rpc_method = rpc_method[0] rpcname = rpc_method.tag.replace(qmap('nc'), "") params = rpc_method.getchildren() paramslen = len(params) if self.debug: logger.debug("%s: RPC: %s: paramslen: %s", str(self), rpcname, str(paramslen)) if rpcname == "close-session": # XXX should be RPC-unlocking if need be if self.debug: logger.debug("%s: Received close-session msg-id: %s", str(self), msg_id) self._send_rpc_reply(etree.Element("ok"), rpc) self.close() # XXX should we also call the user method if it exists? return elif rpcname == "kill-session": # XXX we are supposed to cleanly abort anything underway if self.debug: logger.debug("%s: Received kill-session msg-id: %s", str(self), msg_id) self._send_rpc_reply(etree.Element("ok"), rpc) self.close() # XXX should we also call the user method if it exists? return elif rpcname == "get": # Validate GET parameters if paramslen > 1: # XXX need to specify all elements not known raise ncerror.MalformedMessageRPCError(rpc) if params and not util.filter_tag_match( params[0], "nc:filter"): raise ncerror.UnknownElementProtoError(rpc, params[0]) if not params: params = [None] elif rpcname == "get-config": # Validate GET-CONFIG parameters if paramslen > 2: # XXX Should be ncerror.UnknownElementProtoError? for each? raise ncerror.MalformedMessageRPCError(rpc) source_param = rpc_method.find("nc:source", namespaces=NSMAP) if source_param is None: raise ncerror.MissingElementProtoError( rpc, util.qname("nc:source")) filter_param = None if paramslen == 2: filter_param = rpc_method.find("nc:filter", namespaces=NSMAP) if filter_param is None: unknown_elm = params[ 0] if params[0] != source_param else params[1] raise ncerror.UnknownElementProtoError( rpc, unknown_elm) params = [source_param, filter_param] #------------------ # Call the method. #------------------ try: # Handle any namespaces or prefixes in the tag, other than # "nc" which was removed above. Of course, this does not handle # namespace collisions, but that seems reasonable for now. rpcname = rpcname.rpartition("}")[-1] method_name = "rpc_" + rpcname.replace('-', '_') method = getattr(self.methods, method_name, self._rpc_not_implemented) if self.debug: logger.debug("%s: Calling method: %s", str(self), method_name) reply = method(self, rpc, *params) self._send_rpc_reply(reply, rpc) except NotImplementedError: raise ncerror.OperationNotSupportedProtoError(rpc) except ncerror.MalformedMessageRPCError as msgerr: if self.new_framing: if self.debug: logger.debug("%s: MalformedMessageRPCError: %s", str(self), str(msgerr)) self.send_message(msgerr.get_reply_msg()) else: # If we are 1.0 we have to simply close the connection # as we are not allowed to send this error logger.warning( "Closing 1.0 session due to malformed message") raise ncerror.SessionError(msg, "Malformed message") except ncerror.RPCServerError as error: if self.debug: logger.debug("%s: RPCServerError: %s", str(self), str(error)) self._send_rpc_reply_error(error) except EOFError: if self.debug: logger.debug("%s: Got EOF in reader_handle_message", str(self)) error = ncerror.RPCSvrException(rpc, EOFError("EOF")) self._send_rpc_reply_error(error) except Exception as exception: if self.debug: logger.debug( "%s: Got unexpected exception in reader_handle_message: %s", str(self), str(exception)) error = ncerror.RPCSvrException(rpc, exception) self._send_rpc_reply_error(error)