Пример #1
0
def test_list(reqmanager):
    r1 = Request(Activity(), "14m", "pending request")
    reqmanager.add(r1)
    r2 = Request(Activity(), "2h", "due request")
    r2.state = State.due
    r2.next_due = datetime.datetime(2016, 4, 20, 12, tzinfo=pytz.UTC)
    reqmanager.add(r2)
    r3 = Request(Activity(), "1m 30s", "error request")
    r3.state = State.error
    r3.next_due = datetime.datetime(2016, 4, 20, 11, tzinfo=pytz.UTC)
    att = Attempt()
    att.duration = datetime.timedelta(seconds=75)
    att.returncode = 1
    r3.attempts = [att]
    reqmanager.add(r3)
    assert (
        str(reqmanager)
        == """\
St Id       Scheduled             Estimate  Comment
e  {id3}  2016-04-20 11:00 UTC  1m 30s    error request (duration: 1m 15s)
*  {id2}  2016-04-20 12:00 UTC  2h        due request
-  {id1}  --- TBA ---           14m       pending request\
""".format(
            id1=r1.id[:7], id2=r2.id[:7], id3=r3.id[:7]
        )
    )
Пример #2
0
def test_do_add_two_reqs_with_identical_comments(reqmanager):
    with reqmanager as rm:
        assert rm.add(Request(Activity(), 1, "comment 1")) is not None
        assert (
            rm.add(Request(Activity(), 1, "comment 1"), skip_same_comment=False)
            is not None
        )
        assert len(rm.requests) == 2
Пример #3
0
def test_execute_logs_exception(reqmanager, caplog):
    req = reqmanager.add(Request(Activity(), 1))
    req.state = State.due
    os.chmod(req.dir, 0o000)  # simulates I/O error
    reqmanager.execute()
    assert 'Permission denied' in caplog.text
    os.chmod(req.dir, 0o755)  # py.test cannot clean up 0o000 dirs
Пример #4
0
def test_execute_marks_service_status(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 1))
    req.state = State.due
    reqmanager.execute()
    assert [
        unittest.mock.call(unittest.mock.ANY, False),
        unittest.mock.call(unittest.mock.ANY, True),
    ] == connect().mark_node_service_status.call_args_list
Пример #5
0
def test_explicitly_deleted(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 90))
    req.state = State.deleted
    arch = connect().end_maintenance
    reqmanager.archive()
    arch.assert_called_once_with(
        {req.id: {"duration": None, "result": "deleted"}}
    )
Пример #6
0
def test_duration_from_started_finished(utcnow, tmpdir):
    utcnow.side_effect = [
        datetime.datetime(2016, 4, 20, 6, 0),
        datetime.datetime(2016, 4, 20, 6, 2),
    ]
    r = Request(Activity(), 1, dir=str(tmpdir))
    r.execute()
    assert r.duration == 120.0
Пример #7
0
def test_postpone(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 90))
    req.state = State.postpone
    postp = connect().postpone_maintenance
    reqmanager.postpone()
    postp.assert_called_once_with({req.id: {'postpone_by': 180}})
    assert req.state == State.postpone
    assert req.next_due is None
Пример #8
0
def test_execute_not_performed_on_connection_error(
    execute, connect, reqmanager
):
    connect().mark_node_service_status.side_effect = socket.error()
    req = reqmanager.add(Request(Activity(), 1))
    req.state = State.due
    with pytest.raises(OSError):
        reqmanager.execute()
    assert execute.mock_calls == []
Пример #9
0
def test_duration():
    r = Request(Activity(), 1)
    a = Attempt()
    a.duration = 10
    r.attempts.append(a)
    a = Attempt()
    a.duration = 5
    r.attempts.append(a)
    assert r.duration == 5  # last attempt counts
Пример #10
0
def test_execute_logs_exception(connect, reqmanager, log):
    req = reqmanager.add(Request(Activity(), 1))
    req.state = State.due
    os.chmod(req.dir, 0o000)  # simulates I/O error
    reqmanager.execute()
    log.has(
        "execute-request-failed", request=req.id, level="error", exc_info=True
    )
    os.chmod(req.dir, 0o755)  # py.test cannot clean up 0o000 dirs
Пример #11
0
def test_delete_disappeared_requests(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 1, "comment"))
    sched = connect().schedule_maintenance
    sched.return_value = {
        req.id: {"time": "2016-04-20T15:12:40.9+00:00"},
        "123abc": {"time": None},
    }
    endm = connect().end_maintenance
    reqmanager.schedule()
    endm.assert_called_once_with({"123abc": {"result": "deleted"}})
Пример #12
0
def test_external_activity_state(tmpdir, logger):
    r = Request(ExternalStateActivity(), 1, dir=str(tmpdir))
    r.save()
    extstate = str(tmpdir / "external_state")
    with open(extstate) as f:
        assert "foo\n" == f.read()
    with open(extstate, "w") as f:
        print("bar", file=f)
    r2 = Request.load(str(tmpdir), logger)
    assert r2.activity.external == "bar\n"
Пример #13
0
def test_external_activity_state(tmpdir):
    r = Request(ExternalStateActivity(), 1, dir=str(tmpdir))
    r.save()
    extstate = str(tmpdir / 'external_state')
    with open(extstate) as f:
        assert 'foo\n' == f.read()
    with open(extstate, 'w') as f:
        print('bar', file=f)
    r2 = Request.load(str(tmpdir))
    assert r2.activity.external == 'bar\n'
Пример #14
0
def test_schedule_requests(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 1, "comment"))
    rpccall = connect().schedule_maintenance
    rpccall.return_value = {req.id: {"time": "2016-04-20T15:12:40.9+00:00"}}
    reqmanager.schedule()
    rpccall.assert_called_once_with(
        {req.id: {"estimate": 1, "comment": "comment"}}
    )
    assert req.next_due == datetime.datetime(
        2016, 4, 20, 15, 12, 40, 900000, tzinfo=pytz.UTC
    )
Пример #15
0
def test_execute_obeys_retrylimit(tmpdir):
    Request.MAX_RETRIES = 3
    r = Request(Activity(), 1, dir=str(tmpdir))
    results = []
    for i in range(Request.MAX_RETRIES + 1):
        r.execute()
        assert len(r.attempts) == i + 1
        r.update_state()
        results.append(r.state)
    assert results[0] == State.success
    assert results[-2] == State.success
    assert results[-1] == State.retrylimit
Пример #16
0
def test_execute_activity_no_reboot(connect, run, reqmanager, log):
    activity = Activity()
    req = reqmanager.add(Request(activity, 1))
    req.state = State.due
    reqmanager.execute(run_all_now=True)
    run.assert_has_calls(
        [
            call('echo "entering demo"', shell=True, check=True),
            call('echo "leaving demo"', shell=True, check=True),
        ]
    )
    assert log.has("enter-maintenance")
    assert log.has("leave-maintenance")
Пример #17
0
def request_population(n, dir):
    """Creates a ReqManager with a pregenerated population of N requests.

    The ReqManager and a list of Requests are passed to the calling code.
    """
    with ReqManager(str(dir)) as reqmanager:
        requests = []
        for i in range(n):
            req = Request(Activity(), 60, comment=str(i))
            req._reqid = shortuuid.encode(uuid.UUID(int=i))
            reqmanager.add(req)
            requests.append(req)
        yield (reqmanager, requests)
Пример #18
0
def test_delete_disappeared_requests(connect, reqmanager):
    req = reqmanager.add(Request(Activity(), 1, 'comment'))
    sched = connect().schedule_maintenance
    sched.return_value = {
        req.id: {
            'time': '2016-04-20T15:12:40.9+00:00'
        },
        '123abc': {
            'time': None
        },
    }
    endm = connect().end_maintenance
    reqmanager.schedule()
    endm.assert_called_once_with({'123abc': {'result': 'deleted'}})
Пример #19
0
def test_save_yaml(tmpdir):
    r = Request(Activity(), 10, 'my comment', dir=str(tmpdir))
    assert r.id is not None
    r.save()
    with open(str(tmpdir / 'request.yaml')) as f:
        assert f.read() == """\
!!python/object:fc.maintenance.request.Request
_reqid: {id}
activity: !!python/object:fc.maintenance.activity.Activity {{}}
attempts: []
comment: my comment
dir: {tmpdir}
estimate: !!python/object:fc.maintenance.estimate.Estimate {{value: 10.0}}
""".format(id=r.id, tmpdir=str(tmpdir))
Пример #20
0
def test_execute_activity_with_reboot(connect, run, reqmanager, log):
    activity = Activity()
    activity.reboot_needed = RebootType.WARM
    req = reqmanager.add(Request(activity, 1))
    req.state = State.due
    reqmanager.execute(run_all_now=True)
    run.assert_has_calls(
        [
            call('echo "entering demo"', shell=True, check=True),
            call("reboot", check=True, capture_output=True, text=True),
        ]
    )
    assert log.has("enter-maintenance")
    assert log.has("maintenance-reboot")
    assert not log.has("leave-maintenance")
Пример #21
0
def main():
    a = argparse.ArgumentParser(description=__doc__,
                                epilog="""
If neither a script file or --exec is given, read the activity script from
stdin.
""")
    a.add_argument('-c',
                   '--comment',
                   metavar='TEXT',
                   default='',
                   help='announce upcoming maintenance with this message')
    a.add_argument('-d',
                   '--spooldir',
                   metavar='DIR',
                   default=DEFAULT_DIR,
                   help='request spool dir (default: %(default)s)')
    a.add_argument('estimate',
                   metavar='ESTIMATE',
                   help='estimate activity duration (suffixes: s, m, h)')
    g = a.add_mutually_exclusive_group()
    g.add_argument('-e',
                   '--exec',
                   metavar='SHELLCMD',
                   default=False,
                   help='execute shell command as maintenance activity')
    g.add_argument('file',
                   metavar='FILE',
                   default=None,
                   nargs='?',
                   type=argparse.FileType('r'),
                   help='execute FILE as maintenance activity')
    args = a.parse_args()
    if args.file:
        act = args.file
    elif args.exec:
        act = io.StringIO(args.exec + '\n')
    else:
        act = sys.stdin

    rm = ReqManager(spooldir=args.spooldir)
    rm.add(
        Request(ShellScriptActivity(act), args.estimate, comment=args.comment))
Пример #22
0
def test_save_yaml(tmpdir):
    r = Request(Activity(), 10, "my comment", dir=str(tmpdir))
    assert r.id is not None
    r.save()
    with open(str(tmpdir / "request.yaml")) as f:
        assert (
            f.read()
            == """\
&id001 !!python/object:fc.maintenance.request.Request
_reqid: {id}
_reqmanager: null
activity: !!python/object:fc.maintenance.activity.Activity
  request: *id001
attempts: []
comment: my comment
dir: {tmpdir}
estimate: !!python/object:fc.maintenance.estimate.Estimate
  value: 10.0
""".format(
                id=r.id, tmpdir=str(tmpdir)
            )
        )
Пример #23
0
def main():
    a = argparse.ArgumentParser(description=__doc__)
    a.add_argument('-c',
                   '--comment',
                   metavar='TEXT',
                   default=None,
                   help='announce upcoming reboot with this message')
    a.add_argument('-p',
                   '--poweroff',
                   default=False,
                   action='store_true',
                   help='power off instead of reboot')
    a.add_argument('-d',
                   '--spooldir',
                   metavar='DIR',
                   default=DEFAULT_DIR,
                   help='request spool dir (default: %(default)s)')
    args = a.parse_args()

    action = 'poweroff' if args.poweroff else 'reboot'
    comment = args.comment if args.comment else (
        'Scheduled machine {}'.format(action))
    rm = ReqManager(spooldir=args.spooldir)
    rm.add(Request(RebootActivity(action), 10 * 60, comment=comment))
Пример #24
0
def test_dont_add_two_reqs_with_identical_comments(reqmanager):
    with reqmanager as rm:
        assert rm.add(Request(Activity(), 1, 'comment 1')) is not None
        assert rm.add(Request(Activity(), 1, 'comment 1')) is None
        assert len(rm.requests) == 1
Пример #25
0
def test_execute_proceeds_on_connection_error(connect, reqmanager):
    connect().mark_node_service_status.side_effect = socket.error()
    req = reqmanager.add(Request(Activity(), 1))
    req.state = State.due
    reqmanager.execute()
    assert req.state == State.success
Пример #26
0
def test_duration_from_activity_duration(tmpdir):
    r = Request(FixedDurationActivity(), 1, dir=str(tmpdir))
    r.execute()
    assert r.duration == 90
Пример #27
0
def test_list_other_requests(reqmanager):
    with reqmanager as rm:
        first = rm.add(Request(Activity(), 1))
        second = rm.add(Request(Activity(), 1))
        assert first.other_requests() == [second]
Пример #28
0
def test_update_due_should_not_accept_naive_datetimes():
    r = Request(Activity(), 1)
    with pytest.raises(TypeError):
        r.update_due(datetime.datetime(2016, 4, 20, 12, 00))
Пример #29
0
def test_find_by_comment(reqmanager):
    with reqmanager as rm:
        rm.add(Request(Activity(), 1, 'comment 1'))
        req2 = rm.add(Request(Activity(), 1, 'comment 2'))
    with reqmanager as rm:
        assert req2 == rm.find_by_comment('comment 2')
Пример #30
0
def test_execute_catches_errors(tmpdir):
    r = Request(FailingActivity(), 1, dir=str(tmpdir))
    r.execute()
    assert len(r.attempts) == 1
    assert 'activity failing' in r.attempts[0].stderr
    assert r.attempts[0].returncode != 0