Esempio n. 1
0
    class FakeRpcInterface:
        @typecheck
        def __init__(self, cages_list):
            self._cages_list = cages_list
            self._queue = InterlockedQueue()
            self._pass = 0
            self._stopped = Event()

        def get_cages(self):
            try:
                self._cages = self._cages_list[self._pass]
            except IndexError:
                self._cages = {}
                self._stopped.set()
            self._pass += 1
            return list(self._cages.keys())

        def get_nodes(self, cage):
            return self._cages[cage]

        def process_event(self, node, cage, up_down, probe_result):
            self._queue.push((node, cage, up_down, probe_result))

        def extract_events(self):
            result = {}
            event = self._queue.pop(1.0)
            while event is not None:
                node, cage, up_down, probe_result = event
                events = result.setdefault("{0:s}.{1:s}".format(node, cage), [])
                if up_down == "up":
                    events.append(probe_result)
                else:
                    events.append(None)
                event = self._queue.pop(1.0)
            return result
Esempio n. 2
0
    def test_interface_ordering():

        file_names = [ "{0:08d}.msg".format(i) for i in range(100) ]
        shuffle(file_names)

        for file_name in file_names:
            write_file(file_name, b"data")

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            sleep(0.1)
            loopback_queue.push(request)

        with active_interface("file_1", **interface_config(process_request = process_request,
                              filename_regex = write_prefix + "[0-9a-f]{8}\\.msg")):

            for i in range(100):
                file_name = loopback_queue.pop(3.0)["file_name"]
                assert os_path.basename(file_name).endswith("{0:08d}.msg".format(i))
                if i % 10 == 9:
                    write_file("{0:08d}.msg".format(i // 10), b"data")

            for i in range(10):
                file_name = loopback_queue.pop(3.0)["file_name"]
                assert os_path.basename(file_name).endswith("{0:08d}.msg".format(i))

            assert loopback_queue.pop(3.0) is None
Esempio n. 3
0
    def test_process_timeout():

        loopback_queue = InterlockedQueue()
        delay = Event(); delay.set()

        def process_request(request, response):
            if delay.is_set():
                sleep(pmnc.request.remain + 1.0)
            loopback_queue.push(request)

        with active_interface("jms_1", **interface_config(process_request = process_request,
                              request_timeout = 3.0)):

            fake_request(10.0)

            xa = pmnc.transaction.create()
            xa.jms_1.send("<xml/>")
            message_id = xa.execute()[0]

            assert loopback_queue.pop(3.0) is None

            delay.clear()

            request = loopback_queue.pop(10.0)

        assert request["message_id"] == message_id
        assert request["message_text"] == "<xml/>"
Esempio n. 4
0
    def test_send_many():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            loopback_queue.push(int(request["message_text"]))

        with active_interface("jms_1", **interface_config(process_request = process_request)):

            for i in range(200):

                fake_request(30.0)

                xa = pmnc.transaction.create()
                xa.jms_1.send(str(i*5))
                xa.jms_1.send(str(i*5+1))
                xa.jms_1.send(str(i*5+2))
                xa.jms_1.send(str(i*5+3))
                xa.jms_1.send(str(i*5+4))
                xa.execute()

            received = []
            message_number = loopback_queue.pop(10.0)
            while message_number is not None:
                received.append(message_number)
                message_number = loopback_queue.pop(10.0)

        assert len(received) == 1000
        received_sorted = []
        for i in range(200): # every 5 should have been sent atomically
            received_sorted.extend(list(sorted(received[i*5:(i+1)*5])))
        assert received_sorted == list(range(1000))
Esempio n. 5
0
    def test_process_failure():

        loopback_queue = InterlockedQueue()
        fail = Event(); fail.set()

        def process_request(request, response):
            if fail.is_set():
                sleep(1.0)
                raise Exception("processing failure")
            else:
                loopback_queue.push(request)

        with active_interface("jms_1", **interface_config(process_request = process_request)):

            fake_request(10.0)

            xa = pmnc.transaction.create()
            xa.jms_1.send("<xml/>")
            message_id = xa.execute()[0]

            assert loopback_queue.pop(3.0) is None

            fail.clear()

            request = loopback_queue.pop(10.0)

        assert request["message_id"] == message_id
        assert request["message_text"] == "<xml/>"
        headers = request["headers"]
        assert not headers.get("JMSCorrelationID")
Esempio n. 6
0
 def pop(self, queue: InterlockedQueue): # respects wall-time timeout, see issue9892
     if self.infinite:
         return queue.pop()
     remain = self.remain # there is no special handling for case remain == 0.0, because that
     while remain > 0.0:  # would mean request deadline hence performance is no longer an issue
         result = queue.pop(remain)
         if result is not None:
             return result
         remain = self.remain
     else:
         return None
Esempio n. 7
0
 def pop(self, queue: InterlockedQueue
         ):  # respects wall-time timeout, see issue9892
     if self.infinite:
         return queue.pop()
     remain = self.remain  # there is no special handling for case remain == 0.0, because that
     while remain > 0.0:  # would mean request deadline hence performance is no longer an issue
         result = queue.pop(remain)
         if result is not None:
             return result
         remain = self.remain
     else:
         return None
Esempio n. 8
0
    def test_interface_failure():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            not_defined

        with active_interface("file_1", **interface_config(process_request = process_request,
                              filename_regex = write_prefix + "[0-9a-f]{8}\\.msg")):

            assert loopback_queue.pop(3.0) is None
            file_name = write_file(random_filename() + ".msg", b"data")
            assert os_path.isfile(file_name)
            assert loopback_queue.pop(3.0) is None
            assert os_path.isfile(file_name)
            remove(file_name)
Esempio n. 9
0
    def test_interface_success():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            loopback_queue.push(request)

        with active_interface("file_1", **interface_config(process_request = process_request,
                              filename_regex = write_prefix + "[0-9a-f]{8}\\.msg")):
            assert loopback_queue.pop(3.0) is None
            file_name = write_file(random_filename() + ".msg", b"request")
            assert os_path.isfile(file_name)
            assert loopback_queue.pop(3.0) == dict(file_name = file_name)
            sleep(3.0)
            assert not os_path.exists(file_name)
            assert loopback_queue.pop(3.0) is None
Esempio n. 10
0
    def test_interface_no_wait():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            sleep(5.0)
            loopback_queue.push(request["invocation_time"])

        with active_interface(
                "schedule_1",
                **interface_config(process_request=process_request)):
            dt1 = loopback_queue.pop(9.0)
            dt2 = loopback_queue.pop(6.0)
            dt3 = loopback_queue.pop(6.0)

        assert (dt3 - dt2).seconds == (dt2 - dt1).seconds == 3
Esempio n. 11
0
    def test_interface_success():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            sleep(0.1)
            loopback_queue.push("ok")

        with active_interface(
                "schedule_1",
                **interface_config(process_request=process_request)):
            assert loopback_queue.pop(4.0) == "ok"
            assert loopback_queue.pop(1.0) is None
            assert loopback_queue.pop(3.0) == "ok"
            assert loopback_queue.pop(1.0) is None
            assert loopback_queue.pop(3.0) == "ok"
Esempio n. 12
0
    def drain_queue():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            loopback_queue.push(request)

        with active_interface("jms_1", **interface_config(process_request = process_request)):
            while loopback_queue.pop(10.0) is not None:
                pass
Esempio n. 13
0
    def test_interface_timeout():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            sleep(4.0)
            loopback_queue.push(request)

        with active_interface("file_1", **interface_config(process_request = process_request,
                              filename_regex = write_prefix + "[0-9a-f]{8}\\.msg", request_timeout = 3.0)):

            assert loopback_queue.pop(3.0) is None
            file_name = write_file(random_filename() + ".msg", b"data")
            assert os_path.isfile(file_name)
            assert loopback_queue.pop(3.0) is None
            assert os_path.isfile(file_name)
            assert loopback_queue.pop(4.0) == dict(file_name = file_name)
            assert os_path.isfile(file_name)
            remove(file_name)
Esempio n. 14
0
    def test_post():

        with expected(Exception("the request is no longer pending for response")):
            pmnc.__getattr__(__name__).post("RQ-ABC", "RESULT")

        rs_queue = InterlockedQueue()
        _rs_queues["RQ-ABC"] = rs_queue

        pmnc.__getattr__(__name__).post("RQ-ABC", "RESULT")

        assert rs_queue.pop() == "RESULT"
Esempio n. 15
0
    def test_interface_failure():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            not_defined

        with active_interface(
                "schedule_1",
                **interface_config(process_request=process_request)):
            assert loopback_queue.pop(4.0) is None
Esempio n. 16
0
    def test_post():

        with expected(
                Exception("the request is no longer pending for response")):
            pmnc.__getattr__(__name__).post("RQ-ABC", "RESULT")

        rs_queue = InterlockedQueue()
        _rs_queues["RQ-ABC"] = rs_queue

        pmnc.__getattr__(__name__).post("RQ-ABC", "RESULT")

        assert rs_queue.pop() == "RESULT"
Esempio n. 17
0
    def test_too_large():

        fake_request(10.0)

        q = InterlockedQueue()

        def process_request(request, response):
            q.push(request["packet"])

        with active_interface("udp", **interface_config(process_request = process_request)) as ifc:
            pmnc.transaction.udp_1.send(b"x" * 60000)
            assert q.pop(3.0) is None
Esempio n. 18
0
    def test_success():

        fake_request(10.0)

        q = InterlockedQueue()

        def process_request(request, response):
            q.push(request["packet"])

        with active_interface("udp", **interface_config(process_request = process_request)) as ifc:
            msg = b"foo"
            pmnc.transaction.udp_1.send(msg)
            assert q.pop(3.0) == msg
Esempio n. 19
0
    def test_timeout():

        fake_request(10.0)

        q = InterlockedQueue()

        def process_request(request, response):
            sleep(5.0)

        with active_interface("udp", **interface_config(process_request = process_request)) as ifc:
            msg = b"foo"
            pmnc.transaction.udp_1.send(msg)
            assert q.pop(3.0) is None
Esempio n. 20
0
    def test_deletion_failure():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            loopback_queue.push(request)

        with active_interface("file_1", **interface_config(process_request = process_request,
                              filename_regex = write_prefix + "[0-9a-f]{8}\\.msg")) as ifc:

            ifc._remove_file_ = ifc._remove_file
            ifc._remove_file = lambda file_name: 1 / 0

            file_name = write_file(random_filename() + ".msg", b"data")
            assert loopback_queue.pop(3.0) == dict(file_name = file_name)
            assert loopback_queue.pop(3.0) is None
            assert os_path.isfile(file_name)
            assert ifc._processed_files == { file_name }

            ifc._remove_file = ifc._remove_file_
            assert loopback_queue.pop(3.0) is None
            assert not os_path.exists(file_name)
            assert ifc._processed_files == set()
Esempio n. 21
0
    class FakeRpcInterface:

        @typecheck
        def __init__(self, cages_list):
            self._cages_list = cages_list
            self._queue = InterlockedQueue()
            self._pass = 0
            self._stopped = Event()

        def get_cages(self):
            try:
                self._cages = self._cages_list[self._pass]
            except IndexError:
                self._cages = {}
                self._stopped.set()
            self._pass += 1
            return list(self._cages.keys())

        def get_nodes(self, cage):
            return self._cages[cage]

        def process_event(self, node, cage, up_down, probe_result):
            self._queue.push((node, cage, up_down, probe_result))

        def extract_events(self):
            result = {}
            event = self._queue.pop(1.0)
            while event is not None:
                node, cage, up_down, probe_result = event
                events = result.setdefault("{0:s}.{1:s}".format(node, cage), [])
                if up_down == "up":
                    events.append(probe_result)
                else:
                    events.append(None)
                event = self._queue.pop(1.0)
            return result
Esempio n. 22
0
    def test_process_one():

        loopback_queue = InterlockedQueue()

        def process_request(request, response):
            loopback_queue.push(request)

        with active_interface("jms_1", **interface_config(process_request = process_request)):

            fake_request(10.0)

            xa = pmnc.transaction.create()
            xa.jms_1.send(russian, JMSCorrelationID = russian, FOOBAR = "123")
            message_id = xa.execute()[0]

            request = loopback_queue.pop(10.0)

        assert request["message_id"] == message_id
        assert request["message_text"] == russian
        headers = request["headers"]
        assert headers["JMSCorrelationID"] == russian and headers["FOOBAR"] == "123"
Esempio n. 23
0
class Interface: # SMPP interface

    @typecheck
    def __init__(self, name: str, *,
                 server_address: (str, int),
                 connect_timeout: float,
                 response_timeout: float,
                 ping_interval: optional(float),
                 system_id: str,
                 password: str,
                 system_type: str,
                 esme_ton: byte,
                 esme_npi: byte,
                 esme_addr: str,
                 esme_type: one_of("rcvr", "xmit", "xcvr"),
                 request_timeout: optional(float) = None,
                 **kwargs): # this kwargs allows for extra application-specific
                            # settings in config_interface_smpp_X.py

        self._name = name
        self._response_timeout = response_timeout

        if ping_interval:
            self._ping_timeout = Timeout(ping_interval)
            self._ping_response_timeout = Timeout(response_timeout)
        else:
            self._ping_timeout = self._ping_response_timeout = None
        self._ping_request = None

        self._in_q = InterlockedQueue()
        self._out_q = InterlockedQueue()
        self._inflight = InflightRequests()
        self._ceased = Event()

        if esme_type == "rcvr":
            bind_pdu = BindReceiverPDU
        elif esme_type == "xmit":
            bind_pdu = BindTransmitterPDU
        elif esme_type == "xcvr":
            bind_pdu = BindTransceiverPDU

        self._create_connection = \
            lambda: _SMPPConnection(name, self._in_q, self._out_q, self._inflight,
                                    server_address = server_address,
                                    connect_timeout = connect_timeout,
                                    response_timeout = response_timeout,
                                    system_id = system_id,
                                    password = password,
                                    system_type = system_type,
                                    esme_ton = esme_ton,
                                    esme_npi = esme_npi,
                                    esme_addr = esme_addr,
                                    bind_pdu = bind_pdu)

        self._request_timeout = request_timeout or \
            pmnc.config_interfaces.get("request_timeout") # this is now static

        if pmnc.request.self_test == __name__: # self-test
            self._process_request = kwargs["process_request"]

    name = property(lambda self: self._name)
    ceased = property(lambda self: self._ceased.is_set())

    ###################################

    def start(self):
        self._maintainer = HeavyThread(target = self._maintainer_proc, name = "{0:s}/mnt".format(self.name))
        self._maintainer.start()

    def cease(self):
        self._ceased.set()
        self._maintainer.stop()

    def stop(self):
        pass

    ###################################

    def _maintainer_proc(self):

        while not current_thread().stopped():
            try:

                # try to establish a connection, do it infinitely or until the interface is stopped

                while True:
                    try:
                        self._connection = self._create_connection()
                        self._connection.start()
                    except:
                        pmnc.log.error(exc_string())
                        failure_timeout = max(self._request_timeout, 30.0)
                        if current_thread().stopped(failure_timeout):
                            return
                    else:
                        break # while True

                try:

                    while not current_thread().stopped() and not self._connection.failed:

                        # process incoming PDUs

                        req = self._in_q.pop(1.0)
                        if req is not None:
                            self._handle_pdu(req)

                        # if there is an outstanding ping request, check for response

                        if self._ping_request and self._ping_response_timeout.expired:
                            ping_request, self._ping_request = self._ping_request, None
                            _wait_response(ping_request, 0.001)

                        # if it's time to send another ping request, do so

                        if self._ping_timeout and self._ping_timeout.expired:
                            try:
                                self._ping_request = EnquireLinkPDU.create()
                                self._out_q.push(self._ping_request)
                                self._ping_response_timeout.reset()
                            finally:
                                self._ping_timeout.reset()

                finally:
                    self._connection.stop()

            except:
                pmnc.log.error(exc_string()) # log and ignore

    ###################################
    # this method processes the request PDUs received by this interface from SMSC

    def _handle_pdu(self, req):

        if isinstance(req, EnquireLinkPDU): # respond to pings automatically

            resp = req.create_response()
            self._out_q.push(resp)

        else: # note that this interface does not wait for its requests to complete

            request = pmnc.interfaces.begin_request(
                        timeout = self._request_timeout,
                        interface = self._name, protocol = "smpp",
                        parameters = dict(auth_tokens = dict()),
                        description = "incoming {0:s}".format(req))

            pmnc.interfaces.enqueue(request, self.wu_handle_pdu, (req, ), {})

    ###################################

    @typecheck
    def wu_handle_pdu(self, req: RequestPDU):

        try:

            # see for how long the request was on the execution queue up to this moment
            # and whether it has expired in the meantime, if it did there is no reason
            # to proceed and we simply bail out

            if pmnc.request.expired:
                pmnc.log.error("request has expired and will not be processed")
                success = False
                return # goes through finally section below

            with pmnc.performance.request_processing():
                request = dict(pdu = req)
                response = dict(pdu = req.create_nack(error_codes.ESME_RUNKNOWNERR))
                try:
                    self._process_request(request, response)
                except:
                    response["pdu"] = req.create_nack(error_codes.ESME_RSYSERR)
                    raise
                finally:
                    self._out_q.push(response["pdu"])

        except:
            pmnc.log.error(exc_string()) # log and ignore
            success = False
        else:
            success = True
        finally:                                 # the request ends itself
            pmnc.interfaces.end_request(success) # possibly way after deadline

    ###################################

    def _process_request(self, request, response):
        handler_module_name = "interface_{0:s}".format(self._name)
        pmnc.__getattr__(handler_module_name).process_request(request, response)

    ###################################
    # this method is called by the coupled resources to send a request PDU to SMSC

    @typecheck
    def send(self, req: RequestPDU, timeout: optional(float) = None) -> optional(ResponsePDU):
        self._out_q.push(req)
        if timeout is not None:
            return _wait_response(req, min(timeout, self._response_timeout))
Esempio n. 24
0
 def pop(self, queue: InterlockedQueue
         ):  # respects wall-time timeout, see issue9892
     return queue.pop(self.remain)  # inherits InterlockedQueue's behaviour
Esempio n. 25
0
class AdapterHost:

    @typecheck
    def __init__(self, class_name: str, *,
                 java: os_path.isfile,
                 arguments: tuple_of(str),
                 classpath: str,
                 jndi: dict_of(str, str),
                 factory: str,
                 queue: str,
                 username: str,
                 password: str):

        # random line prefix and suffix for packet serialization

        bol = b2a_hex(urandom(8)).decode("ascii").upper()
        eol = b2a_hex(urandom(8)).decode("ascii").upper()

        # compose the executable command line

        self._args = [ java ] + list(arguments) + \
                     [ "-classpath", classpath, class_name,
                       "connection.factory={0:s}".format(factory),
                       "connection.queue={0:s}".format(queue),
                       "stdout.bol={0:s}".format(bol),
                       "stdout.eol={0:s}".format(eol) ]

        if username or password:
            self._args.append("connection.username={0:s}".format(username))
            self._args.append("connection.password={0:s}".format(password))

        self._args.extend("jndi.{0:s}={1:s}".format(*t) for t in jndi.items())

        self._bol_b = bol.encode("ascii")
        self._eol_b = eol.encode("ascii")

        # this set tracks messages that have been processed but
        # not committed on the server due to a failure

        self._processed_messages = set()

    ###################################

    # adapter input from stdin consists of Packet sequence

    def _stdin_writer_proc(self, stdin, stdin_queue):
        try:
            try:
                pkt = stdin_queue.pop()
                while pkt is not None: # this light thread exits upon None in the queue or exception
                    pkt.save_to_stream(stdin, 128)
                    if pkt.get("XPmncResponse") == "COMMIT":
                        message_id = pkt.pop("XPmncMessageID")
                        self._processed_messages.remove(message_id)
                    pkt = stdin_queue.pop()
            finally:
                stdin.close()
        except:
            pmnc.log.error(exc_string()) # log and ignore

    ###################################

    # adapter output from stdout consists of Packet sequence

    def _stdout_reader_proc(self, stdout, stdin_queue):
        try:
            try:
                try:
                    pkt = Packet.load_from_stream(stdout, self._bol_b, self._eol_b)
                    while pkt is not None: # this light thread exits upon eof or exception
                        self._stdout_queue.push(pkt)
                        pkt = Packet.load_from_stream(stdout, self._bol_b, self._eol_b)
                finally:
                    stdout.close()
            finally:
                stdin_queue.push(None) # this releases the associated stdin writer
        except:
            pmnc.log.error(exc_string()) # log and ignore

    ###################################

    # adapter output from stderr is discarded (yet it has to be read)

    def _stderr_reader_proc(self, stderr):
        try:
            try:
                while stderr.read(512): # this light thread exits only upon exception
                    pass
            finally:
                stderr.close()
        except:
            pmnc.log.error(exc_string()) # log and ignore

    ###################################

    # the adapter is considered up until the process exits

    def _adapter_running(self):

        return self._adapter.poll() is None

    ###################################

    # this method starts the adapter process, creates the handling
    # threads and waits for the adapter to report its readiness

    def _start_adapter(self, adapter_usage, adapter_name, start_timeout):

        pmnc.log.info("starting adapter process for {0:s} {1:s}".\
                      format(adapter_usage, adapter_name))

        if pmnc.log.debug:
            pmnc.log.debug("adapter process command line: {0:s}".format(" ".join(self._args)))

        self._adapter = popen(*self._args) # start the java process
        try:

            if pmnc.log.debug:
                pmnc.log.debug("adapter process (pid {0:d}) has started".\
                               format(self._adapter.pid))

            # the process has started and its initialization is underway
            # create light threads for controlling stdin/out/err, these
            # threads are not stopped explicitly but exit when the adapter
            # exits and the pipes break

            self._stdin_queue = InterlockedQueue()
            self._stdin_writer = LightThread(target = self._stdin_writer_proc,
                                             args = (self._adapter.stdin, self._stdin_queue),
                                             name = "{0:s}:stdin".format(adapter_name))
            self._stdin_writer.start()

            self._stdout_queue = InterlockedQueue()
            self._stdout_reader = LightThread(target = self._stdout_reader_proc,
                                              args = (self._adapter.stdout, self._stdin_queue),
                                              name = "{0:s}:stdout".format(adapter_name))
            self._stdout_reader.start()

            self._stderr_queue = InterlockedQueue()
            self._stderr_reader = LightThread(target = self._stderr_reader_proc,
                                              args = (self._adapter.stderr, ),
                                              name = "{0:s}:stderr".format(adapter_name))
            self._stderr_reader.start()

            # wait for the adapter to come up and report readiness

            while not start_timeout.expired:
                pkt = self._stdout_queue.pop(min(0.5, start_timeout.remain))
                if pkt is None: # see whether the adapter has exited
                    if not self._adapter_running():
                        retcode = self._adapter.wait()
                        if retcode is not None:
                            raise Exception("adapter process exited with "
                                            "retcode {0:d}".format(retcode))
                elif "XPmncError" in pkt:
                    raise Exception(pkt["XPmncError"])
                elif pkt.get("XPmncStatus") == "READY":
                    break
                else:
                    raise Exception("adapter process returned invalid status")
            else:
                raise Exception("timeout waiting for adapter process to initialize")

        except:
            self._stop_adapter(start_timeout) # what remained of start timeout is used for stopping
            raise

        pmnc.log.info("adapter process (pid {0:d}) is ready".format(self._adapter.pid))

    ###################################

    # if not exited peacefully, adapter has to be killed

    def _stop_adapter(self, stop_timeout):

        while self._adapter_running() and not stop_timeout.expired:
            sleep(min(0.5, stop_timeout.remain))

        if self._adapter_running():
            pmnc.log.warning("killing runaway adapter process "
                             "(pid {0:d})".format(self._adapter.pid))
            self._adapter.kill()
        else:
            pmnc.log.info("adapter process (pid {0:d}) exited with retcode {1:d}".\
                          format(self._adapter.pid, self._adapter.wait()))
Esempio n. 26
0
 def pop(self, queue: InterlockedQueue): # respects wall-time timeout, see issue9892
     return queue.pop(self.remain) # inherits InterlockedQueue's behaviour