Skip to content

Commit

Permalink
Merge pull request saltstack#66442 from hurzhurz/3006.x-wintask-fixes
Browse files Browse the repository at this point in the history
[3006.x] fix win_task ExecutionTimeLimit and result/error code interpretation
  • Loading branch information
dmurphy18 authored Jul 2, 2024
2 parents 7cbfcf0 + 43c32e6 commit 3618966
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 11 deletions.
1 change: 1 addition & 0 deletions changelog/66347.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix win_task ExecutionTimeLimit and result/error code interpretation
1 change: 1 addition & 0 deletions changelog/66441.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix win_task ExecutionTimeLimit and result/error code interpretation
40 changes: 29 additions & 11 deletions salt/modules/win_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ def __virtual__():
return False, "Module win_task: module only works on Windows systems"


def _signed_to_unsigned_int32(code):
"""
Convert negative result and error codes from win32com
"""
if code < 0:
code = code + 2**32
return code


def _get_date_time_format(dt_string):
"""
Copied from win_system.py (_get_date_time_format)
Expand Down Expand Up @@ -253,6 +262,8 @@ def _reverse_lookup(dictionary, value):
value_index = idx
break

if value_index < 0:
return "invalid value"
return list(dictionary)[value_index]


Expand Down Expand Up @@ -311,19 +322,20 @@ def _save_task_definition(

except pythoncom.com_error as error:
hr, msg, exc, arg = error.args # pylint: disable=W0633
error_code = _signed_to_unsigned_int32(exc[5])
fc = {
-2147024773: (
0x8007007B: (
"The filename, directory name, or volume label syntax is incorrect"
),
-2147024894: "The system cannot find the file specified",
-2147216615: "Required element or attribute missing",
-2147216616: "Value incorrectly formatted or out of range",
-2147352571: "Access denied",
0x80070002: "The system cannot find the file specified",
0x80041319: "Required element or attribute missing",
0x80041318: "Value incorrectly formatted or out of range",
0x80020005: "Access denied",
}
try:
failure_code = fc[exc[5]]
failure_code = fc[error_code]
except KeyError:
failure_code = f"Unknown Failure: {error}"
failure_code = f"Unknown Failure: {hex(error_code)}"

log.debug("Failed to modify task: %s", failure_code)

Expand Down Expand Up @@ -683,7 +695,7 @@ def create_task_from_xml(

except pythoncom.com_error as error:
hr, msg, exc, arg = error.args # pylint: disable=W0633
error_code = hex(exc[5] + 2**32)
error_code = _signed_to_unsigned_int32(exc[5])
fc = {
0x80041319: "Required element or attribute missing",
0x80041318: "Value incorrectly formatted or out of range",
Expand Down Expand Up @@ -731,7 +743,7 @@ def create_task_from_xml(
try:
failure_code = fc[error_code]
except KeyError:
failure_code = f"Unknown Failure: {error_code}"
failure_code = f"Unknown Failure: {hex(error_code)}"
finally:
log.debug("Failed to create task: %s", failure_code)
raise CommandExecutionError(failure_code)
Expand Down Expand Up @@ -1469,10 +1481,16 @@ def info(name, location="\\"):
task_folder = task_service.GetFolder(location)
task = task_folder.GetTask(name)

last_task_result_code = _signed_to_unsigned_int32(task.LastTaskResult)
try:
last_task_result = results[last_task_result_code]
except KeyError:
last_task_result = f"Unknown Task Result: {hex(last_task_result_code)}"

properties = {
"enabled": task.Enabled,
"last_run": _get_date_value(task.LastRunTime),
"last_run_result": results[task.LastTaskResult],
"last_run_result": last_task_result,
"missed_runs": task.NumberOfMissedRuns,
"next_run": _get_date_value(task.NextRunTime),
"status": states[task.State],
Expand All @@ -1494,7 +1512,7 @@ def info(name, location="\\"):
duration, def_set.DeleteExpiredTaskAfter
)

if def_set.ExecutionTimeLimit == "":
if def_set.ExecutionTimeLimit == "" or def_set.ExecutionTimeLimit == "PT0S":
settings["execution_time_limit"] = False
else:
settings["execution_time_limit"] = _reverse_lookup(
Expand Down
91 changes: 91 additions & 0 deletions tests/pytests/unit/modules/test_win_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest

import salt.modules.win_task as win_task
from salt.exceptions import CommandExecutionError

pytestmark = [
pytest.mark.skip_unless_on_windows,
Expand Down Expand Up @@ -85,3 +86,93 @@ def test_edit_task_delete_after(base_task):

result = win_task.info(base_task)
assert result["settings"]["delete_after"] is False


def test_execution_time_limit(base_task):
result = win_task.add_trigger(
base_task,
trigger_type="Daily",
trigger_enabled=True,
end_date=datetime.today().strftime("%Y-%m-%d"),
end_time="23:59:59",
)
assert result is True

result = win_task.edit_task(base_task, execution_time_limit="1 hour")
assert result is True

result = win_task.info(base_task)
assert result["settings"]["execution_time_limit"] == "1 hour"

result = win_task.edit_task(base_task, execution_time_limit=False)
assert result is True

result = win_task.info(base_task)
assert result["settings"]["execution_time_limit"] is False


@pytest.mark.parametrize(
"exitcode, expect",
[
(0, "The operation completed successfully"),
(3221225786, "The application terminated as a result of CTRL+C"),
(4289449455, "Unknown Task Result: 0xffabcdef"),
],
)
def test_run_result_code(exitcode, expect):
task_name = "SaltTest"
try:
result = win_task.create_task(
task_name,
user_name="System",
force=True,
action_type="Execute",
cmd="cmd.exe",
arguments=f"/c exit {exitcode}",
)
assert result is True

result = win_task.info(task_name)
assert result["last_run_result"] == "Task has not yet run"

result = win_task.run_wait(task_name)
assert result is True

result = win_task.info(task_name)
assert result["last_run_result"] == expect
finally:
result = win_task.delete_task(task_name)
assert result is True


def test_create_task_from_xml():
task_name = "SaltTest"
task_xml = '<Task xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"><Actions><Exec><Command>cmd.exe</Command><Arguments>/c exit</Arguments></Exec></Actions></Task>'
try:
result = win_task.create_task_from_xml(
task_name, user_name="System", xml_text=task_xml
)
assert result is True

result = win_task.info(task_name)
assert result["actions"][0]["action_type"] == "Execute"
assert result["actions"][0]["cmd"] == "cmd.exe"
assert result["actions"][0]["arguments"] == "/c exit"

finally:
result = win_task.delete_task(task_name)
assert result is True


def test_create_task_from_xml_error():
task_name = "SaltTest"
try:
with pytest.raises(CommandExecutionError) as excinfo:
result = win_task.create_task_from_xml(
task_name, user_name="System", xml_text="test"
)
assert result is False
assert "The task XML is malformed" in str(excinfo.value)
finally:
result = win_task.delete_task(task_name)
assert result is not True

0 comments on commit 3618966

Please sign in to comment.