mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #65084 from garethgreenaway/65033_schedule_state_documentation
[3006.x] Allow schedule state module to update schedule when the minion is offline
This commit is contained in:
commit
05b10ec9cd
3 changed files with 518 additions and 26 deletions
1
changelog/65033.fixed.md
Normal file
1
changelog/65033.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
Allow schedule state module to update schedule when the minion is offline.
|
|
@ -214,11 +214,15 @@ def present(name, **kwargs):
|
|||
Whether the scheduled job should run immediately after the skip_during_range time
|
||||
period ends.
|
||||
|
||||
offline
|
||||
Add the scheduled job to the Salt minion when the Salt minion is not running.
|
||||
"""
|
||||
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": []}
|
||||
|
||||
current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False)
|
||||
current_schedule = __salt__["schedule.list"](
|
||||
show_all=True, return_yaml=False, offline=kwargs.get("offline")
|
||||
)
|
||||
|
||||
if name in current_schedule:
|
||||
new_item = __salt__["schedule.build_schedule_item"](name, **kwargs)
|
||||
|
@ -289,7 +293,9 @@ def absent(name, **kwargs):
|
|||
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": []}
|
||||
|
||||
current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False)
|
||||
current_schedule = __salt__["schedule.list"](
|
||||
show_all=True, return_yaml=False, offline=kwargs.get("offline")
|
||||
)
|
||||
if name in current_schedule:
|
||||
if "test" in __opts__ and __opts__["test"]:
|
||||
kwargs["test"] = True
|
||||
|
@ -357,11 +363,15 @@ def disabled(name, **kwargs):
|
|||
persist
|
||||
Whether changes to the scheduled job should be saved, defaults to True.
|
||||
|
||||
offline
|
||||
Delete the scheduled job to the Salt minion when the Salt minion is not running.
|
||||
"""
|
||||
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": []}
|
||||
|
||||
current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False)
|
||||
current_schedule = __salt__["schedule.list"](
|
||||
show_all=True, return_yaml=False, offline=kwargs.get("offline")
|
||||
)
|
||||
if name in current_schedule:
|
||||
if "test" in __opts__ and __opts__["test"]:
|
||||
kwargs["test"] = True
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
"""
|
||||
:codeauthor: Jayesh Kariya <jayeshk@saltstack.com>
|
||||
:codeauthor: Gareth J. Greenaway <ggreenaway@vmware.com>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import salt.modules.schedule as schedule_mod
|
||||
import salt.states.schedule as schedule
|
||||
from tests.support.mock import MagicMock, patch
|
||||
from salt.utils.odict import OrderedDict
|
||||
from tests.support.mock import MagicMock, mock_open, patch
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -19,49 +22,527 @@ def test_present():
|
|||
"""
|
||||
name = "job1"
|
||||
|
||||
ret = {"name": name, "changes": {}, "result": False, "comment": ""}
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{}, {"job1": job1}])
|
||||
|
||||
mock_build_schedule = OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "job1"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
)
|
||||
|
||||
mock_add = {
|
||||
"comment": "Added job: test-schedule to schedule.",
|
||||
"result": True,
|
||||
"changes": {"test-schedule": "added"},
|
||||
}
|
||||
|
||||
mock_dict = MagicMock(side_effect=[ret, []])
|
||||
mock_mod = MagicMock(return_value=ret)
|
||||
mock_lst = MagicMock(side_effect=[{name: {}}, {name: {}}, {}, {}])
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.build_schedule_item": mock_dict,
|
||||
"schedule.modify": mock_mod,
|
||||
"schedule.add": mock_mod,
|
||||
"schedule.build_schedule_item": MagicMock(return_value=mock_build_schedule),
|
||||
"schedule.add": MagicMock(return_value=mock_add),
|
||||
},
|
||||
):
|
||||
assert schedule.present(name) == ret
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {"test-schedule": "added"},
|
||||
"comment": "Adding new job job1 to schedule",
|
||||
}
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
with patch.dict(schedule.__opts__, {"test": False}):
|
||||
assert schedule.present(name) == ret
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
"comment": "Job job1 in correct state",
|
||||
}
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
assert schedule.present(name) == ret
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
job1_update = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "6:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{"job1": job1}, {"job1": job1_update}])
|
||||
|
||||
mock_build_schedule = OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "job1"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
)
|
||||
|
||||
mock_modify = {
|
||||
"comment": "Modified job: test-schedule in schedule.",
|
||||
"changes": {
|
||||
"test-schedule": {
|
||||
"old": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
),
|
||||
"new": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
),
|
||||
}
|
||||
},
|
||||
"result": True,
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.build_schedule_item": MagicMock(return_value=mock_build_schedule),
|
||||
"schedule.modify": MagicMock(return_value=mock_modify),
|
||||
},
|
||||
):
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {
|
||||
"test-schedule": {
|
||||
"old": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
),
|
||||
"new": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
),
|
||||
}
|
||||
},
|
||||
"comment": "Modifying job job1 in schedule",
|
||||
}
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
"comment": "Job job1 in correct state",
|
||||
}
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{}])
|
||||
|
||||
mock_build_schedule = OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "job1"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
)
|
||||
|
||||
mock_add = {
|
||||
"comment": "Job: test-schedule would be added to schedule.",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.build_schedule_item": MagicMock(return_value=mock_build_schedule),
|
||||
"schedule.add": MagicMock(return_value=mock_add),
|
||||
},
|
||||
):
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
"comment": "Job: test-schedule would be added to schedule.",
|
||||
}
|
||||
with patch.dict(schedule.__opts__, {"test": True}):
|
||||
ret.update({"result": True})
|
||||
assert schedule.present(name) == ret
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
job1_update = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "6:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{"job1": job1}, {"job1": job1_update}])
|
||||
|
||||
mock_build_schedule = OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "job1"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
)
|
||||
|
||||
mock_modify = {
|
||||
"comment": "Job: test-schedule would be modified in schedule.",
|
||||
"changes": {
|
||||
"test-schedule": {
|
||||
"old": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
),
|
||||
"new": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
),
|
||||
}
|
||||
},
|
||||
"result": True,
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.build_schedule_item": MagicMock(return_value=mock_build_schedule),
|
||||
"schedule.modify": MagicMock(return_value=mock_modify),
|
||||
},
|
||||
):
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {
|
||||
"test-schedule": {
|
||||
"old": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
),
|
||||
"new": OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "test-schedule"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "6:00am"),
|
||||
]
|
||||
),
|
||||
}
|
||||
},
|
||||
"comment": "Job: test-schedule would be modified in schedule.",
|
||||
}
|
||||
with patch.dict(schedule.__opts__, {"test": True}):
|
||||
_res = schedule.present(name)
|
||||
assert _res == ret
|
||||
|
||||
# Add job to schedule when offline=True
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
"offline": True,
|
||||
}
|
||||
mock_lst = MagicMock(return_value={})
|
||||
|
||||
mock_build_schedule = OrderedDict(
|
||||
[
|
||||
("function", "test.ping"),
|
||||
("maxrunning", 1),
|
||||
("name", "job1"),
|
||||
("enabled", True),
|
||||
("jid_include", True),
|
||||
("when", "4:00am"),
|
||||
]
|
||||
)
|
||||
|
||||
mock_add = {
|
||||
"comment": "Adding new job test-schedule to schedule.",
|
||||
"result": True,
|
||||
"changes": {"test-schedule": "added"},
|
||||
}
|
||||
|
||||
event_enter = MagicMock()
|
||||
event_enter.send.side_effect = (lambda data, tag, cb=None, timeout=60: True,)
|
||||
event = MagicMock()
|
||||
event.__enter__.return_value = event_enter
|
||||
|
||||
with patch("salt.utils.event.get_event", return_value=event):
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.build_schedule_item": MagicMock(
|
||||
return_value=mock_build_schedule
|
||||
),
|
||||
"schedule.add": MagicMock(return_value=mock_add),
|
||||
},
|
||||
):
|
||||
with patch.object(schedule_mod, "list_", mock_lst):
|
||||
with patch.object(
|
||||
schedule_mod,
|
||||
"_get_schedule_config_file",
|
||||
MagicMock(return_value="/etc/salt/minion.d/_schedule.conf"),
|
||||
):
|
||||
with patch("salt.utils.files.fopen", mock_open()):
|
||||
ret = {
|
||||
"comment": "Adding new job job1 to schedule",
|
||||
"result": True,
|
||||
"name": "job1",
|
||||
"changes": {"test-schedule": "added"},
|
||||
}
|
||||
|
||||
_res = schedule.present(name, offline=True)
|
||||
assert _res == ret
|
||||
assert event.call_count == 0
|
||||
|
||||
|
||||
def test_absent():
|
||||
"""
|
||||
Test to ensure a job is absent from the schedule.
|
||||
"""
|
||||
|
||||
# Delete job from schedule
|
||||
name = "job1"
|
||||
|
||||
ret = {"name": name, "changes": {}, "result": False, "comment": ""}
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{"job1": job1}])
|
||||
|
||||
mock_delete = {
|
||||
"comment": "Deleted job test-schedule from schedule.",
|
||||
"result": True,
|
||||
"changes": {"test-schedule": "removed"},
|
||||
}
|
||||
|
||||
mock_mod = MagicMock(return_value=ret)
|
||||
mock_lst = MagicMock(side_effect=[{name: {}}, {}])
|
||||
with patch.dict(
|
||||
schedule.__salt__, {"schedule.list": mock_lst, "schedule.delete": mock_mod}
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.delete": MagicMock(return_value=mock_delete),
|
||||
},
|
||||
):
|
||||
with patch.dict(schedule.__opts__, {"test": False}):
|
||||
assert schedule.absent(name) == ret
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {"test-schedule": "removed"},
|
||||
"comment": "Removed job job1 from schedule",
|
||||
}
|
||||
_res = schedule.absent(name)
|
||||
assert _res == ret
|
||||
|
||||
# Delete job from schedule when job does not exist
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{}])
|
||||
|
||||
mock_delete = {
|
||||
"comment": "Job test-schedule does not exist.",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.delete": MagicMock(return_value=mock_delete),
|
||||
},
|
||||
):
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
"comment": "Job job1 not present in schedule",
|
||||
}
|
||||
_res = schedule.absent(name)
|
||||
assert _res == ret
|
||||
|
||||
# Delete job from schedule when test=True
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
}
|
||||
mock_lst = MagicMock(side_effect=[{"job1": job1}])
|
||||
|
||||
mock_delete = {
|
||||
"comment": "Job: job1 would be deleted from schedule.",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.delete": MagicMock(return_value=mock_delete),
|
||||
},
|
||||
):
|
||||
ret = {
|
||||
"name": "job1",
|
||||
"result": True,
|
||||
"changes": {},
|
||||
"comment": "Job: job1 would be deleted from schedule.",
|
||||
}
|
||||
|
||||
with patch.dict(schedule.__opts__, {"test": True}):
|
||||
comt = "Job job1 not present in schedule"
|
||||
ret.update({"comment": comt, "result": True})
|
||||
assert schedule.absent(name) == ret
|
||||
_res = schedule.absent(name)
|
||||
assert _res == ret
|
||||
|
||||
# Delete job from schedule when offline=True
|
||||
job1 = {
|
||||
"function": "test.ping",
|
||||
"maxrunning": 1,
|
||||
"name": "job1",
|
||||
"enabled": True,
|
||||
"jid_include": True,
|
||||
"when": "4:00am",
|
||||
"offline": True,
|
||||
}
|
||||
mock_lst = MagicMock(return_value={"job1": job1})
|
||||
|
||||
mock_delete = {
|
||||
"comment": "Deleted Job job1 from schedule.",
|
||||
"result": True,
|
||||
"changes": {"job1": "removed"},
|
||||
}
|
||||
|
||||
event_enter = MagicMock()
|
||||
event_enter.send.side_effect = (lambda data, tag, cb=None, timeout=60: True,)
|
||||
event = MagicMock()
|
||||
event.__enter__.return_value = event_enter
|
||||
|
||||
with patch("salt.utils.event.get_event", return_value=event):
|
||||
with patch.dict(
|
||||
schedule.__salt__,
|
||||
{
|
||||
"schedule.list": mock_lst,
|
||||
"schedule.delete": schedule_mod.delete,
|
||||
},
|
||||
):
|
||||
with patch.object(schedule_mod, "list_", mock_lst):
|
||||
with patch.object(
|
||||
schedule_mod,
|
||||
"_get_schedule_config_file",
|
||||
MagicMock(return_value="/etc/salt/minion.d/_schedule.conf"),
|
||||
):
|
||||
with patch("salt.utils.files.fopen", mock_open()):
|
||||
ret = {
|
||||
"comment": "Removed job job1 from schedule",
|
||||
"result": True,
|
||||
"name": "job1",
|
||||
"changes": {"job1": "removed"},
|
||||
}
|
||||
|
||||
_res = schedule.absent(name, offline=True)
|
||||
assert _res == ret
|
||||
assert event.call_count == 0
|
||||
|
|
Loading…
Add table
Reference in a new issue