Ejemplo n.º 1
0
    def test_repeating(self, exclusive):
        PERIOD = 0.1
        TIMES = 3

        invocations = [0, 0]
        invoked = threading.Event()

        def _work():
            invocations[0] += 1
            invocations[1] = monotonic_time()
            if invocations[0] == TIMES:
                invoked.set()

        op = periodic.Operation(_work,
                                period=PERIOD,
                                scheduler=self.sched,
                                executor=self.exc,
                                exclusive=exclusive)
        op.start()
        invoked.wait(PERIOD * TIMES + PERIOD)
        # depending on timing, _work may be triggered one more time.
        # nothing prevents this, although is unlikely.
        # we don't care of this case
        op.stop()
        self.assertTrue(invoked.is_set())
        self.assertTrue(TIMES <= invocations[0] <= TIMES + 1)
Ejemplo n.º 2
0
    def test_repeating_exclusive_with_pool_exhausted(self):
        PERIOD = 0.1
        TRIES_BEFORE_SUCCESS = 2

        exc = _RecoveringExecutor(tries_before_success=TRIES_BEFORE_SUCCESS)

        attempts = [0]
        done = threading.Event()

        def _work():
            attempts[0] = exc.attempts
            logging.info('_work invoked after %d attempts', attempts[0])
            done.set()

        op = periodic.Operation(_work, period=PERIOD,
                                scheduler=self.sched,
                                executor=exc,
                                exclusive=True)
        op.start()
        timeout = 2  # seconds
        # We intentionally using a timeout much longer than actually needed
        # the timeout should be >= PERIOD * (TRIES_BEFORE_SUCCESS + 1).
        # We use larger value to reduce the chance of false failures
        # on overloaded CI workers.
        self.assertTrue(done.wait(timeout))
        self.assertEqual(attempts[0], TRIES_BEFORE_SUCCESS + 1)
        op.stop()
Ejemplo n.º 3
0
    def test_repeating_after_block(self):
        PERIOD = 0.1
        TIMES = 5
        BLOCK_AT = 2

        invocations = [0, 0]
        executions = [0, 0]
        done = threading.Event()

        def _work():
            invocations[0] += 1
            invocations[1] = monotonic_time()
            if invocations[0] == BLOCK_AT:
                # must be > (PERIOD * TIMES) ~= forever
                time.sleep(10 * PERIOD * TIMES)
            executions[0] += 1
            executions[1] = monotonic_time()
            if invocations[0] == TIMES:
                done.set()

        op = periodic.Operation(_work,
                                period=PERIOD,
                                scheduler=self.sched,
                                executor=self.exc)
        op.start()
        done.wait(PERIOD * TIMES + PERIOD)
        # depending on timing, _work may be triggered one more time.
        # nothing prevents this, although is unlikely.
        # we don't care of this case
        op.stop()
        self.assertTrue(done.is_set())
        self.assertTrue(executions[1] >= invocations[1])
        self.assertTrue(TIMES <= invocations[0] <= TIMES + 1)
        # one execution never completed
        self.assertEqual(executions[0], invocations[0] - 1)
Ejemplo n.º 4
0
    def start(self):
        if not config.getboolean('guest_agent', 'enable_qga_poller'):
            self.log.info('Not starting QEMU-GA poller. It is disabled in'
                          ' configuration')
            return

        def per_vm_operation(job, period):
            disp = periodic.VmDispatcher(self._cif.getVMs, self._executor,
                                         lambda vm: job(vm, self),
                                         _TASK_TIMEOUT)
            return periodic.Operation(disp,
                                      period,
                                      self._scheduler,
                                      timeout=_TASK_TIMEOUT,
                                      executor=self._executor)

        self._operations = [
            periodic.Operation(self._cleanup,
                               config.getint('guest_agent', 'cleanup_period'),
                               self._scheduler,
                               executor=self._executor),

            # Monitor what QEMU-GA offers
            per_vm_operation(CapabilityCheck,
                             config.getint('guest_agent', 'qga_info_period')),
        ]

        self.log.info("Starting QEMU-GA poller")
        self._executor.start()
        for op in self._operations:
            op.start()
Ejemplo n.º 5
0
    def test_dump_executor_state_on_resource_exhausted(self):
        PERIOD = 0.1
        MAX_TASKS = 20  # random value

        log = fakelib.FakeLogger()

        exc = executor.Executor(name="test.Executor",
                                # intentional we  just want to clog the queue
                                workers_count=0,
                                max_tasks=MAX_TASKS,
                                scheduler=self.sched,  # unused
                                max_workers=0,
                                log=log)
        exc.start()

        op = periodic.Operation(lambda: None,
                                period=PERIOD,
                                scheduler=self.sched,
                                executor=exc,
                                timeout=None,
                                exclusive=False)
        with MonkeyPatchScope([
            (throttledlog, '_logger', log),
        ]):
            # the first dispatch is done here
            op.start()
            for _ in range(MAX_TASKS - 1):
                op._dispatch()
            # this will trigger the exception, and the dump
            op._dispatch()
        level, message, args = log.messages[-1]
        self.assertTrue(message.startswith('executor state:'))
Ejemplo n.º 6
0
 def test_invalid_period(self):
     op = periodic.Operation(lambda: None,
                             period=0,
                             scheduler=self.sched,
                             executor=self.exc)
     with pytest.raises(periodic.InvalidValue):
         op.start()
Ejemplo n.º 7
0
 def per_vm_operation(job, period):
     disp = periodic.VmDispatcher(
         self._cif.getVMs, self._executor,
         lambda vm: job(vm, self),
         _TASK_TIMEOUT)
     return periodic.Operation(
         disp, period, self._scheduler, timeout=_TASK_TIMEOUT,
         executor=self._executor)
Ejemplo n.º 8
0
    def test_start_with_invalid_operation(self):
        """
        periodic.start() should swallow any error that
        periodic.Operation.start() may raise, and keep starting
        the other operations after the failed one.
        """
        lock = threading.Lock()
        done = threading.Event()

        def _work():
            with lock:
                self.tasks -= 1
                if not self.tasks:
                    done.set()

        ops = [
            periodic.Operation(_work,
                               period=1.0,
                               scheduler=self.sched,
                               executor=self.exc),

            # will raise periodic.InvalidValue
            periodic.Operation(lambda: None, period=0,
                               scheduler=self.sched,
                               executor=self.exc),

            periodic.Operation(_work,
                               period=1.0,
                               scheduler=self.sched,
                               executor=self.exc),
        ]

        with MonkeyPatchScope([
            (periodic, 'config',
                make_config([('sampling', 'enable', 'false')])),
            (periodic, '_create', lambda cif, sched: ops),
        ]):
            # Don't assume operations are started in order,
            # we just know all of them will be start()ed.
            # See the documentation of periodic.start()
            periodic.start(fake.ClientIF(), self.sched)

        done.wait(0.5)
        self.assertTrue(done.is_set())
Ejemplo n.º 9
0
    def test_start_twice(self):
        def _work():
            pass

        op = periodic.Operation(_work,
                                period=1.0,
                                scheduler=self.sched,
                                executor=self.exc)
        op.start()
        self.assertRaises(AssertionError, op.start)
Ejemplo n.º 10
0
    def test_start(self):
        invoked = threading.Event()

        def _work():
            invoked.set()

        op = periodic.Operation(_work, period=1.0,
                                scheduler=self.sched,
                                executor=self.exc)
        op.start()
        invoked.wait(0.5)
        self.assertTrue(invoked.is_set())
Ejemplo n.º 11
0
    def start(self):
        if not config.getboolean('guest_agent', 'enable_qga_poller'):
            self.log.info('Not starting QEMU-GA poller. It is disabled in'
                          ' configuration')
            return

        def per_vm_operation(job, period):
            disp = periodic.VmDispatcher(self._cif.getVMs, self._executor,
                                         lambda vm: job(vm, self),
                                         _TASK_TIMEOUT)
            return periodic.Operation(disp,
                                      period,
                                      self._scheduler,
                                      timeout=_TASK_TIMEOUT,
                                      executor=self._executor)

        self._operations = [
            periodic.Operation(self._cleanup,
                               config.getint('guest_agent', 'cleanup_period'),
                               self._scheduler,
                               executor=self._executor),

            # Monitor what QEMU-GA offers
            per_vm_operation(CapabilityCheck,
                             config.getint('guest_agent', 'qga_info_period')),

            # Basic system information
            per_vm_operation(
                SystemInfoCheck,
                config.getint('guest_agent', 'qga_sysinfo_period')),
            per_vm_operation(
                NetworkInterfacesCheck,
                config.getint('guest_agent', 'qga_sysinfo_period')),

            # List of active users
            per_vm_operation(
                ActiveUsersCheck,
                config.getint('guest_agent', 'qga_active_users_period')),

            # Filesystem info and disk mapping
            per_vm_operation(
                DiskInfoCheck,
                config.getint('guest_agent', 'qga_disk_info_period')),
        ]

        self.log.info("Starting QEMU-GA poller")
        self._executor.start()
        for op in self._operations:
            op.start()
Ejemplo n.º 12
0
 def start(self):
     if not config.getboolean('guest_agent', 'enable_qga_poller'):
         self.log.info('Not starting QEMU-GA poller. It is disabled in'
                       ' configuration')
         return
     self._operation = periodic.Operation(
         self._poller,
         config.getint('guest_agent', 'qga_polling_period'),
         self._scheduler,
         timeout=_TASK_TIMEOUT,
         executor=self._executor,
         exclusive=True)
     self.log.info("Starting QEMU-GA poller")
     self._executor.start()
     self._operation.start()
Ejemplo n.º 13
0
    def test_repeating_if_raises(self):
        PERIOD = 0.1
        TIMES = 5

        def _work():
            pass

        exc = _FakeExecutor(fail=True, max_attempts=TIMES)
        op = periodic.Operation(_work, period=PERIOD,
                                scheduler=self.sched,
                                executor=exc)
        op.start()
        completed = exc.done.wait(PERIOD * TIMES + PERIOD)
        # depending on timing, _work may be triggered one more time.
        # nothing prevents this, although is unlikely.
        # we don't care of this case
        op.stop()
        self.assertTrue(completed)
        self.assertTrue(TIMES <= exc.attempts <= TIMES + 1)
Ejemplo n.º 14
0
    def test_repeating_exclusive_operation(self):
        PERIOD = 0.2

        executions = [0]
        ready = threading.Event()
        done = threading.Event()

        log = logging.getLogger('test')

        def _work():
            n = executions[0]
            executions[0] += 1
            log.info('BEGIN _work() n=%d', n)
            if n == 0:
                # block just the first time
                # we intentionally don't set done
                # to emulate lost worker
                log.info('waiting for readiness...')
                ready.wait()
                log.info('ready!')
            else:
                done.set()
                log.info('done!')
            log.info('END')

        op = periodic.Operation(_work,
                                period=PERIOD,
                                scheduler=self.sched,
                                executor=self.exc,
                                timeout=None,
                                exclusive=True)
        op.start()
        self.assertFalse(done.wait(PERIOD * 4))
        # we just wait "long enough" to make sure we cross at least one
        # timeout threshold.
        ready.set()
        completed = done.wait(PERIOD * 2)  # guard against races
        op.stop()
        self.assertTrue(completed)
        # op.stop() doesn't guarantee the immediate termination, so _work()
        # can run one extra time
        self.assertGreaterEqual(executions[0], 2)
Ejemplo n.º 15
0
    def test_stop(self):
        PERIOD = 0.1

        invocations = [0]

        def _work():
            invocations[0] = monotonic_time()

        op = periodic.Operation(_work, period=PERIOD,
                                scheduler=self.sched,
                                executor=self.exc)
        op.start()
        time.sleep(PERIOD * 2)
        # avoid pathological case on which nothing ever runs
        self.assertTrue(invocations[0] > 0)

        op.stop()

        # cooldown. Let's try to avoid scheduler mistakes.
        time.sleep(PERIOD)
        stop = monotonic_time()

        self.assertTrue(stop > invocations[0])
Ejemplo n.º 16
0
 def test_invalid_period(self):
     op = periodic.Operation(lambda: None, period=0,
                             scheduler=self.sched,
                             executor=self.exc)
     self.assertRaises(periodic.InvalidValue, op.start)