Exemple #1
0
    def _push(self, msg, code=None):
        promise = Promise()

        with self._lock:
            if self._should_stop != self._ST_NONE:
                promise.set(exc=ServiceStopped())
            else:
                self._do_push(msg, promise, code)

        return promise.to_future()
Exemple #2
0
    def __lookup_tags(self, tags, as_bools):
        ret = {}

        cloud_tags = set()

    # FIXME not as closure
        def _ret_value(state):
            if as_bools:
                if not state:
                    return False
                elif isinstance(state, TagBase):
                    return state.IsLocallySet()
                else:
                    return state.is_set
            else:
                if not state:
                    return None
                elif isinstance(state, TagBase):
                    return {'is_set': state.IsLocallySet()}
                else:
                    return state.__dict__

        for tag in tags:
            # FIXME Consider that tag may exists as LocalTag in infile_items or inmem_items?
            if self._is_cloud_tag_name(tag):
                cloud_tags.add(tag)
            else:
                # dont_create=True to distinguish unset tags from non-existed
                ret[tag] = _ret_value(self._RawTag(tag, dont_create=True))

        promise = Promise()

        if not cloud_tags:
            promise.set(ret)
            return promise.to_future()

        cloud_done = self._cloud.lookup(cloud_tags)

        def on_cloud_done(f):
            if f.is_success():
                cloud_result = f.get()
                for tag in cloud_tags:
                    ret[tag] = _ret_value(cloud_result.get(tag, None))
                promise.set(ret)
            else:
                promise.set(None, f.get_exception())

        cloud_done.subscribe(on_cloud_done)

        return promise.to_future()
Exemple #3
0
class SerialUpdateOnlyDummyCloudClient(object):
    """This class allows SafeCloud to work correctly"""

    def __init__(self, wrong_setup_exc):
        self._wrong_setup_future = Promise().set(None, wrong_setup_exc).to_future()
        self._serial_update_promise = Promise()
        self._serial_update_future = self._serial_update_promise.to_future()

    def stop(self, timeout=None):
        self._serial_update_promise.set(None, RuntimeError("cloud client stopped"))

    def is_stopped(self):
        return self._serial_update_future.is_set()

    def wait_outgoing_empty(self):
        raise NotImplementedError()

    def serial_update(self, update):
        return self._serial_update_future

    def debug_server(self):
        return self._wrong_setup_future

    def subscribe(self, tags, with_future=True):
        return READY_FUTURE

    def unsubscribe(self, tags, with_future=True):
        return READY_FUTURE

    def lookup(self, tags):
        return self._wrong_setup_future

    def match(self, prefix=None, regexp=None, limit=None):
        return self._wrong_setup_future

    def update(self, updates):
        return self._wrong_setup_future
Exemple #4
0
    def __init__(self, server_pid, channel): # executor_pid, executor_stderr
        self._server_pid = server_pid
        self._server_exit_status = None
        self._channel = channel

        self._channel_in  = dupfdopen(channel, 'r')
        self._channel_out = dupfdopen(channel, 'w')

        self._errored = Bool()
        self._input_queue = deque()
        self._lock = threading.Lock()
        self._queue_not_empty = threading.Condition(self._lock)
        self._should_stop = Bool()
        self._next_task_id = 1
        self._tasks = {}

        self._fail = self._create_fail()

        self._server_stop = Promise()

        self._read_thread_inited = threading.Event()
        self._write_thread_inited = threading.Event()

        write_thread = ProfiledThread(
            target=_weak_method(self._write_loop),
            name_prefix='RunnerClnWr')

        self._write_thread = weakref.ref(write_thread)

        read_thread = ProfiledThread(
            target=_weak_method(self._read_loop),
            name_prefix='RunnerClnRd')

        self._read_thread = weakref.ref(read_thread)

        write_thread.daemon = True
        read_thread.daemon = True

        write_thread.start()
        read_thread.start()

        self._read_thread_inited.wait()
        self._write_thread_inited.wait()
Exemple #5
0
class _Client(object):
    class RunTask(object):
        def __init__(self):
            self.pid = Promise()
            self.term_info = Promise()

        def list_promises(self):
            return [self.pid, self.term_info]

    class SignalTask(object):
        def __init__(self):
            self.ack = Promise()

        def list_promises(self):
            return [self.ack]

    def __init__(self, server_pid, channel): # executor_pid, executor_stderr
        self._server_pid = server_pid
        self._server_exit_status = None
        self._channel = channel

        self._channel_in  = dupfdopen(channel, 'r')
        self._channel_out = dupfdopen(channel, 'w')

        self._errored = Bool()
        self._input_queue = deque()
        self._lock = threading.Lock()
        self._queue_not_empty = threading.Condition(self._lock)
        self._should_stop = Bool()
        self._next_task_id = 1
        self._tasks = {}

        self._fail = self._create_fail()

        self._server_stop = Promise()

        self._read_thread_inited = threading.Event()
        self._write_thread_inited = threading.Event()

        write_thread = ProfiledThread(
            target=_weak_method(self._write_loop),
            name_prefix='RunnerClnWr')

        self._write_thread = weakref.ref(write_thread)

        read_thread = ProfiledThread(
            target=_weak_method(self._read_loop),
            name_prefix='RunnerClnRd')

        self._read_thread = weakref.ref(read_thread)

        write_thread.daemon = True
        read_thread.daemon = True

        write_thread.start()
        read_thread.start()

        self._read_thread_inited.wait()
        self._write_thread_inited.wait()

    def _create_fail(self):
        errored = self._errored
        queue_not_empty = self._queue_not_empty
        channel = self._channel
        tasks = self._tasks

        # Don't closure on self to prevent cycle
        def fail():
            with queue_not_empty:
                if errored:
                    return

                errored.set()

                try:
                    channel.shutdown(socket.SHUT_RDWR)
                except:
                    pass

                exc = ServiceUnavailable("Runner abnormal termination")

                tasks_values = tasks.values()
                tasks.clear()

                for task in tasks_values:
                    for p in task.list_promises():
                        if not p.is_set():
                            try:
                                p.set(None, exc)
                            except:
                                pass

                queue_not_empty.notify_all()

        return fail

    def _wait_stop(self):
        for w in [self._read_thread, self._write_thread]:
            t = w()
            if t:
                t.join()
        _, status = os.waitpid(self._server_pid, 0)
        logging.info('_Server exited with %s' % status)
        if status:
            raise RuntimeError("Runner.Server process exited abnormally %d" % status)

    def stop(self):
        do_wait = False

        with self._lock:
            if not self._should_stop:
                self._should_stop.set()
                self._queue_not_empty.notify()
                do_wait = True

        if do_wait:
            self._server_stop.run_and_set(self._wait_stop)

        self._server_stop.to_future().get()

    def __del__(self):
        self.stop()

    def _get_next_task_id(self):
        ret = self._next_task_id
        self._next_task_id += 1
        return ret

    def _enqueue(self, task, msg):
        with self._lock:
            if self._errored:
                raise ServiceUnavailable("Runner in malformed state")

            task_id = self._get_next_task_id()

            msg.task_id = task_id
            self._tasks[task_id] = task

            self._input_queue.append(msg)
            self._queue_not_empty.notify()

            return task_id

    def start(self, *pargs, **pkwargs):
        task = self.RunTask()
        task_id = self._enqueue(task, NewTaskParamsMessage(*pargs, **pkwargs))

        def join_pid():
            pid = task.pid.to_future().get()

            return _Popen(
                pid=pid,
                task_id=task_id,
                exit_status=task.term_info.to_future(),
                client=self # FIXME weak
            )

        return join_pid

    def Popen(self, *pargs, **pkwargs):
        def start():
            return self.start(*pargs, **pkwargs)()

        stdin_content = pkwargs.pop('stdin_content', None)

        if stdin_content is None:
            return start()

    # FIXME Why 'stdin_content' supported only in Popen?
    # TODO Reimplement: use named pipes?
        with NamedTemporaryFile() as tmp:
            tmp.write(stdin_content)
            tmp.flush()

            pkwargs['stdin'] = tmp.name

            return start()

    def call(self, *args, **kwargs):
        return self.Popen(*args, **kwargs).wait()

    def check_call(self, *args, **kwargs):
        return check_process_call(self.call, args, kwargs) # TODO

    def _send_signal(self, target_task, sig, group):
        task = self.SignalTask()
        self._enqueue(task, SendSignalRequestMessage(target_task._task_id, sig, group))
        return task.ack.to_future()

    @fail_on_error
    def _write_loop(self):
        queue = self._input_queue
        channel_out = self._channel_out
        lock = self._lock
        queue_not_empty = self._queue_not_empty
        channel = self._channel
        should_stop = self._should_stop
        errored = self._errored
        self._write_thread_inited.set()
        del self # don't use self

        logging.debug('_Client._write_loop started')

        while True:
            with lock:
                while not (should_stop or queue or errored):
                    queue_not_empty.wait()

                if errored:
                    raise RuntimeError("Errored in another thread")

                messages = []
                while queue:
                    messages.append(queue.popleft())

                if should_stop:
                    messages.append(StopServiceRequestMessage())
                    logging.debug('_Client._write_loop append(StopServiceRequestMessage)')

            for msg in messages:
                serialize(channel_out, msg)
            channel_out.flush()

            if should_stop:
                break

        channel.shutdown(socket.SHUT_WR)

        logging.debug('_Client._write_loop finished')

    @fail_on_error
    def _read_loop(self):
        channel_in = self._channel_in
        lock = self._lock
        tasks = self._tasks
        should_stop = self._should_stop
        channel = self._channel
        errored = self._errored
        self._read_thread_inited.set()
        del self # don't use self

        stop_response_received = False

        logging.debug('_Client._read_loop started')

        while True:
            try:
                msg = deserialize(channel_in)
            except EOFError:
                logging.debug('_Client._read_loop EOFError')
                break

            #logging.debug('_Client._read_loop %s' % msg)

            if isinstance(msg, ProcessStartMessage):
                #logging.debug('_Client._read_loop ProcessStartMessage for task_id=%d, pid=%d, received'  % (msg.task_id, msg.pid))

                pid, error = msg.pid, msg.error

                if error:
                    #error = RuntimeError(error) # FIXME pickle Exception?
                    pass # TODO Check! :)

                with lock:
                    try:
                        task = tasks[msg.task_id] if pid else tasks.pop(msg.task_id)
                    except KeyError:
                        if errored:
                            continue
                        else:
                            raise
                    task.pid.set(pid, error)

            elif isinstance(msg, ProcessTerminationMessage):
                #logging.info('_Client._read_loop ProcessTerminationMessage for task_id=%d, received'  % msg.task_id)

                with lock:
                    try:
                        task = tasks.pop(msg.task_id)
                    except KeyError:
                        if errored: # FIXME Don't remember
                            continue
                        else:
                            raise

            # FIXME Better?
                p = task.term_info
                if isinstance(msg.exit_status, Exception):
                    p.set(None, msg.exit_status)
                else:
                    p.set(msg.exit_status)

            elif isinstance(msg, SendSignalResponseMessage):
                with lock:
                    try:
                        task = tasks.pop(msg.task_id)
                    except KeyError:
                        if errored:
                            continue
                        else:
                            raise

                task.ack.set(msg.was_sent, msg.error)

            elif isinstance(msg, StopServiceResponseMessage):
                logging.debug('_Client._read_loop StopServiceResponseMessage received')

                if not should_stop: # FIXME more fine check
                    raise RuntimeError('StopServiceResponseMessage received but StopServiceRequestMessage was not send')
                if tasks:
                    raise RuntimeError('StopServiceResponseMessage received but not for all process replices was received')

                stop_response_received = True

            else:
                raise RuntimeError('Unknown message type')

        if not stop_response_received:
            raise RuntimeError('Socket closed without StopServiceResponseMessage')

        channel.shutdown(socket.SHUT_RD)
        logging.debug('_Client._read_loop finished')
Exemple #6
0
 def add(self, update, with_future=False):
     queue = self._queues[hash(update[0].GetFullname()) % len(self._queues)]
     promise = Promise() if with_future else None
     queue.put((update, promise))
     if promise:
         return promise.to_future()
Exemple #7
0
 def __init__(self, wrong_setup_exc):
     self._wrong_setup_future = Promise().set(None, wrong_setup_exc).to_future()
     self._serial_update_promise = Promise()
     self._serial_update_future = self._serial_update_promise.to_future()