Example #1
0
    def _open_session(self, is_server):
        assert is_server or self.session_id is None

        # The transport should be connected at this point.
        try:
            # Send hello message.
            self.send_hello((NC_BASE_10, NC_BASE_11), self.session_id)

            # Get reply
            reply = self._receive_message()
            if self.debug:
                logger.debug("Received HELLO")

            # Parse reply
            tree = etree.parse(io.BytesIO(reply.encode('utf-8')))
            root = tree.getroot()
            caps = root.xpath("//nc:hello/nc:capabilities/nc:capability",
                              namespaces=NSMAP)

            # Store capabilities
            for cap in caps:
                self.capabilities.add(cap.text)

            if NC_BASE_11 in self.capabilities:
                self.new_framing = True
            elif NC_BASE_10 not in self.capabilities:
                raise SessionError(
                    "Server doesn't implement 1.0 or 1.1 of netconf")

            # Get session ID.
            try:
                session_id = root.xpath("//nc:hello/nc:session-id",
                                        namespaces=NSMAP)[0].text
                # If we are a server it is a failure to receive a session id.
                if is_server:
                    raise SessionError("Client sent a session-id")
                self.session_id = int(session_id)
            except (KeyError, IndexError, AttributeError):
                if not is_server:
                    raise SessionError("Server didn't supply session-id")
            except ValueError:
                raise SessionError(
                    "Server supplied non integer session-id: {}", session_id)

            self.session_open = True

            # Create reader thread.
            self.reader_thread = threading.Thread(
                target=self._read_message_thread)
            self.reader_thread.daemon = True
            self.reader_thread.keep_running = True
            self.reader_thread.start()

            if self.debug:
                logger.debug("%s: Opened version %s session.", str(self),
                             "1.1" if self.new_framing else "1.0")

        except Exception:
            self.close()
            raise
Example #2
0
    def _reader_handle_message(self, msg):
        """This function is called from the session reader thread to process a received
        framed netconf message.
        """
        try:
            tree = etree.parse(io.BytesIO(msg.encode('utf-8')))
            if not tree:
                raise SessionError(msg, "Invalid XML from server.")
        except etree.XMLSyntaxError:
            raise SessionError(msg, "Invalid XML from server.")

        replies = tree.xpath("/nc:rpc-reply", namespaces=NSMAP)
        if not replies:
            raise SessionError(msg, "No rpc-reply found")

        for reply in replies:
            try:
                msg_id = int(reply.get(qmap("nc") + 'message-id'))
            except (TypeError, ValueError):
                try:
                    # Deal with servers not properly setting attribute namespace.
                    msg_id = int(reply.get('message-id'))
                except (TypeError, ValueError):
                    # # Cisco is returning errors without message-id attribute which
                    # # is non-rfc-conforming it is doing this for any malformed XML
                    # # not simply missing message-id attribute.
                    # error = reply.xpath("nc:rpc-error", namespaces=self.nsmap)
                    # if error:
                    #     raise RPCError(received, tree, error[0])
                    raise SessionError(msg,
                                       "No valid message-id attribute found")

            # Queue the message
            with self.cv:
                try:
                    if msg_id not in self.rpc_out:
                        if self.debug:
                            logger.debug(
                                "Ignoring unwanted reply for message-id %s",
                                str(msg_id))
                        return
                    elif self.rpc_out[msg_id] is not None:
                        logger.warning(
                            "Received multiple replies for message-id %s:"
                            " before: %s now: %s", str(msg_id),
                            str(self.rpc_out[msg_id]), str(msg))

                    if self.debug:
                        logger.debug("%s: Received rpc-reply message-id: %s",
                                     str(self), str(msg_id))
                    self.rpc_out[msg_id] = tree, reply, msg
                except Exception as error:
                    logger.debug("%s: Unexpected exception: %s", str(self),
                                 str(error))
                    raise
                finally:
                    self.cv.notify_all()
    def reader_handle_message(self, msg):
        """Handle a message, lock is already held"""
        try:
            tree = etree.parse(io.BytesIO(msg.encode('utf-8')))
            if not tree:
                raise SessionError(msg, "Invalid XML from server.")
        except etree.XMLSyntaxError:
            raise SessionError(msg, "Invalid XML from server.")

        replies = tree.xpath("/nc:rpc-reply", namespaces=NSMAP)
        if not replies:
            notification = tree.xpath("/*[name()='notification']")
            if not notification:
                raise SessionError(msg, "No rpc-reply or notification found")
            else:
                logger.info("* NOTIF received *")
                logger.info(etree.tostring(tree, pretty_print=True))
                return

        for reply in replies:
            try:
                msg_id = int(reply.get('message-id'))
            except (TypeError, ValueError):
                # # Cisco is returning errors without message-id attribute which is non-rfc-conforming
                # # it is doing this for any malformed XML not simply missing message-id attribute.
                # error = reply.xpath("nc:rpc-error", namespaces=self.nsmap)
                # if error:
                #     raise RPCError(received, tree, error[0])
                raise SessionError(msg, "No valid message-id attribute found")

            # Queue the message
            with self.cv:
                try:
                    if msg_id not in self.rpc_out:
                        if self.debug:
                            logger.debug(
                                "Ignoring unwanted reply for message-id %s",
                                str(msg_id))
                        return
                    elif self.rpc_out[msg_id] is not None:
                        logger.warning(
                            "Received multiple replies for message-id %s:"
                            " before: %s now: %s", str(msg_id),
                            str(self.rpc_out[msg_id]), str(msg))

                    if self.debug:
                        logger.debug("%s: Received rpc-reply message-id: %s",
                                     str(self), str(msg_id))
                    self.rpc_out[msg_id] = tree, reply, msg
                except Exception as error:
                    logger.debug("%s: Unexpected exception: %s", str(self),
                                 str(error))
                    raise
                finally:
                    self.cv.notify_all()
Example #4
0
    def wait_reply(self, msg_id, timeout=None):
        assert msg_id in self.rpc_out

        check_timeout = Timeout(timeout)

        self.cv.acquire()
        # XXX need to make sure the channel doesn't close
        while self.rpc_out[msg_id] is None and self.is_active():
            remaining = check_timeout.remaining()

            self.cv.wait(remaining)
            if self.rpc_out[msg_id] is not None:
                break

            if check_timeout.is_expired():
                raise ReplyTimeoutError(
                    "Timeout ({}s) while waiting for RPC reply to msg-id: {}".
                    format(timeout, msg_id))

        if not self.is_active():
            self.cv.release()
            raise SessionError("Session closed while waiting for reply")

        tree, reply, msg = self.rpc_out[msg_id]
        del self.rpc_out[msg_id]
        self.cv.release()

        error = reply.xpath("nc:rpc-error", namespaces=NSMAP)
        if error:
            raise RPCError(msg, tree, error[0])

        # data = reply.xpath("nc:data", namespaces=self.nsmap)
        # ok = reply.xpath("nc:ok", namespaces=self.nsmap)
        return tree, reply, msg
Example #5
0
    def _handle_message (self, msg):
        """Handle a message, lock is already held"""
        try:
            tree = etree.parse(io.BytesIO(msg.encode('utf-8')))
            if not tree:
                raise SessionError(msg, "Invalid XML from server.")
        except etree.XMLSyntaxError:
            raise SessionError(msg, "Invalid XML from server.")

        replies = tree.xpath("/nc:rpc-reply", namespaces=NSMAP)
        if not replies:
            raise SessionError(msg, "No rpc-reply found")

        for reply in replies:
            try:
                msg_id = int(reply.get('message-id'))
            except (TypeError, ValueError):
                # # Cisco is returning errors without message-id attribute which is non-rfc-conforming
                # # it is doing this for any malformed XML not simply missing message-id attribute.
                # error = reply.xpath("nc:rpc-error", namespaces=self.nsmap)
                # if error:
                #     raise RPCError(received, tree, error[0])
                raise SessionError(msg, "No valid message-id attribute found")

            if msg_id not in self.rpc_out:
                if self.debug:
                    logger.debug("Ignoring unwanted reply for message-id %s", str(msg_id))
                return
            elif self.rpc_out[msg_id] is not None:
                logger.warning("Received multiple replies for message-id %s: before: %s now: %s",
                               str(msg_id), str(self.rpc_out[msg_id]), str(msg))

            if self.debug:
                logger.debug("%s: Received rpc-reply message-id: %s",
                             str(self),
                             str(msg_id))
            self.rpc_out[msg_id] = tree, reply, msg
Example #6
0
    def wait_reply(self, msg_id, timeout=None):
        """Wait for a reply to a given RPC message ID.

        :param msg_id: the RPC message ID returned from one of the async method calls
        :return: (Message as an lxml tree, Parsed reply content, Parsed message content).
        :rtype: (lxml.etree, lxml.Element, lxml.Element)
        :raises: RPCError, SessionError
        """
        assert msg_id in self.rpc_out

        check_timeout = Timeout(timeout)

        self.cv.acquire()
        # XXX need to make sure the channel doesn't close
        while self.rpc_out[msg_id] is None and self.is_active():
            remaining = check_timeout.remaining()

            self.cv.wait(remaining)
            if self.rpc_out[msg_id] is not None:
                break

            if check_timeout.is_expired():
                raise ReplyTimeoutError(
                    "Timeout ({}s) while waiting for RPC reply to msg-id: {}".
                    format(timeout, msg_id))

        if not self.is_active():
            self.cv.release()
            raise SessionError("Session closed while waiting for reply")

        tree, reply, msg = self.rpc_out[msg_id]
        del self.rpc_out[msg_id]
        self.cv.release()

        error = reply.xpath("nc:rpc-error", namespaces=NSMAP)
        if error:
            raise RPCError(msg, tree, error[0])

        # data = reply.xpath("nc:data", namespaces=self.nsmap)
        # ok = reply.xpath("nc:ok", namespaces=self.nsmap)
        return tree, reply, msg
Example #7
0
    def wait_reply (self, msg_id):
        assert msg_id in self.rpc_out

        self.cv.acquire()
        # XXX need to make sure the channel doesn't close
        while self.rpc_out[msg_id] is None and self.is_active():
            self.cv.wait()

        if not self.is_active():
            raise SessionError("Session closed while waiting for reply")

        tree, reply, msg = self.rpc_out[msg_id]
        del self.rpc_out[msg_id]
        self.cv.release()

        error = reply.xpath("nc:rpc-error", namespaces=NSMAP)
        if error:
            raise RPCError(msg, tree, error[0])

        # data = reply.xpath("nc:data", namespaces=self.nsmap)
        # ok = reply.xpath("nc:ok", namespaces=self.nsmap)
        return tree, reply, msg
Example #8
0
 def is_reply_ready(self, msg_id):
     """Check whether reply is ready (or session closed)"""
     with self.cv:
         if not self.is_active():
             raise SessionError("Session closed while checking for reply")
         return self.rpc_out[msg_id] is not None
Example #9
0
 def is_reply_ready (self, msg_id):
     """Check whether reply is ready (or session closed)"""
     if not self.is_active():
         raise SessionError("Session closed while checking for reply")
     with self.cv:
         return self.is_reply_ready_locked(msg_id)
Example #10
0
    def _reader_handle_message(self, msg):
        """This function is called from the session reader thread to process a received
        framed netconf message.
        """
        try:
            tree = etree.parse(io.BytesIO(msg.encode('utf-8')))
            if not tree:
                raise SessionError(msg, "Invalid XML from server.")
        except etree.XMLSyntaxError:
            raise SessionError(msg, "Invalid XML from server.")

        replies = tree.xpath("/nc:rpc-reply", namespaces=NSMAP)
        tmp_NS = {'tmp': 'urn:ietf:params:xml:ns:netconf:notification:1.0'}
        notifications = tree.xpath("/tmp:notification", namespaces=tmp_NS)
        if not replies and not notifications:
            raise SessionError(msg, "No rpc-reply found")

        for reply in replies:
            try:
                msg_id = int(reply.get(qmap("nc") + 'message-id'))
            except (TypeError, ValueError):
                try:
                    # Deal with servers not properly setting attribute namespace.
                    msg_id = int(reply.get('message-id'))
                except (TypeError, ValueError):
                    # # Cisco is returning errors without message-id attribute which
                    # # is non-rfc-conforming it is doing this for any malformed XML
                    # # not simply missing message-id attribute.
                    # error = reply.xpath("nc:rpc-error", namespaces=self.nsmap)
                    # if error:
                    #     raise RPCError(received, tree, error[0])
                    raise SessionError(msg,
                                       "No valid message-id attribute found")

            # Queue the message
            with self.cv:
                try:
                    if msg_id not in self.rpc_out:
                        if self.debug:
                            logger.debug(
                                "Ignoring unwanted reply for message-id %s",
                                str(msg_id))
                        return
                    elif self.rpc_out[msg_id] is not None:
                        logger.warning(
                            "Received multiple replies for message-id %s:"
                            " before: %s now: %s", str(msg_id),
                            str(self.rpc_out[msg_id]), str(msg))

                    if self.debug:
                        logger.debug("%s: Received rpc-reply message-id: %s",
                                     str(self), str(msg_id))
                    self.rpc_out[msg_id] = tree, reply, msg
                except Exception as error:
                    logger.debug("%s: Unexpected exception: %s", str(self),
                                 str(error))
                    raise
                finally:
                    self.cv.notify_all()

        for notif in notifications:
            push_update_node = notif.find("yp:push-update", namespaces=NSMAP)
            id_node = push_update_node.find("tmp:id", namespaces=tmp_NS)
            subscription_id = int(id_node.text)

            with self.notification_queues_lock:
                if subscription_id not in self.notification_queues:
                    tmp_queue = self.tmp_notification_queues.get(
                        subscription_id, Queue())
                    tmp_queue.put(notif)
                    self.tmp_notification_queues[subscription_id] = tmp_queue
                else:
                    self.notification_queues[subscription_id].put(notif)