File: //proc/self/root/usr/lib/python2.7/site-packages/salt/modules/schedule.py
# -*- coding: utf-8 -*-
'''
Module for managing the Salt schedule on a minion
.. versionadded:: 2014.7.0
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import copy as pycopy
import datetime
import difflib
import logging
import os
try:
import dateutil.parser as dateutil_parser
_WHEN_SUPPORTED = True
_RANGE_SUPPORTED = True
except ImportError:
_WHEN_SUPPORTED = False
_RANGE_SUPPORTED = False
# Import salt libs
import salt.utils.event
import salt.utils.files
import salt.utils.odict
import salt.utils.yaml
# Import 3rd-party libs
from salt.ext import six
__proxyenabled__ = ['*']
log = logging.getLogger(__name__)
__func_alias__ = {
'list_': 'list',
'reload_': 'reload'
}
SCHEDULE_CONF = [
'name',
'maxrunning',
'function',
'splay',
'range',
'when',
'once',
'once_fmt',
'returner',
'jid_include',
'args',
'kwargs',
'_seconds',
'seconds',
'minutes',
'hours',
'days',
'enabled',
'return_job',
'metadata',
'cron',
'until',
'after',
'return_config',
'return_kwargs',
'run_on_start',
'skip_during_range',
'run_after_skip_range',
]
def list_(show_all=False,
show_disabled=True,
where=None,
return_yaml=True):
'''
List the jobs currently scheduled on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.list
# Show all jobs including hidden internal jobs
salt '*' schedule.list show_all=True
# Hide disabled jobs from list of jobs
salt '*' schedule.list show_disabled=False
'''
schedule = {}
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'func': 'list',
'where': where}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_list_complete', wait=30)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret = {}
ret['comment'] = 'Event module not available. Schedule list failed.'
ret['result'] = True
log.debug('Event module not available. Schedule list failed.')
return ret
_hidden = ['enabled',
'skip_function',
'skip_during_range']
for job in list(schedule.keys()): # iterate over a copy since we will mutate it
if job in _hidden:
continue
# Default jobs added by salt begin with __
# by default hide them unless show_all is True.
if job.startswith('__') and not show_all:
del schedule[job]
continue
# if enabled is not included in the job,
# assume job is enabled.
if 'enabled' not in schedule[job]:
schedule[job]['enabled'] = True
for item in pycopy.copy(schedule[job]):
if item not in SCHEDULE_CONF:
del schedule[job][item]
continue
if schedule[job][item] is None:
del schedule[job][item]
continue
if schedule[job][item] == 'true':
schedule[job][item] = True
if schedule[job][item] == 'false':
schedule[job][item] = False
# if the job is disabled and show_disabled is False, skip job
if not show_disabled and not schedule[job]['enabled']:
del schedule[job]
continue
if '_seconds' in schedule[job]:
# remove _seconds from the listing
del schedule[job]['_seconds']
if schedule:
if return_yaml:
tmp = {'schedule': schedule}
return salt.utils.yaml.safe_dump(tmp, default_flow_style=False)
else:
return schedule
else:
return {'schedule': {}}
def is_enabled(name):
'''
List a Job only if its enabled
.. versionadded:: 2015.5.3
CLI Example:
.. code-block:: bash
salt '*' schedule.is_enabled name=job_name
'''
current_schedule = __salt__['schedule.list'](show_all=False, return_yaml=False)
if name in current_schedule:
return current_schedule[name]
else:
return {}
def purge(**kwargs):
'''
Purge all the jobs currently scheduled on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.purge
'''
ret = {'comment': [],
'result': True}
for name in list_(show_all=True, return_yaml=False):
if name == 'enabled':
continue
if name.startswith('__'):
continue
if 'test' in kwargs and kwargs['test']:
ret['result'] = True
ret['comment'].append('Job: {0} would be deleted from schedule.'.format(name))
else:
persist = kwargs.get('persist', True)
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'name': name,
'func': 'delete',
'persist': persist}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_delete_complete', wait=30)
if event_ret and event_ret['complete']:
_schedule_ret = event_ret['schedule']
if name not in _schedule_ret:
ret['result'] = True
ret['comment'].append('Deleted job: {0} from schedule.'.format(name))
else:
ret['comment'].append('Failed to delete job {0} from schedule.'.format(name))
ret['result'] = True
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule add failed.'
ret['result'] = True
return ret
def delete(name, **kwargs):
'''
Delete a job from the minion's schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.delete job1
'''
ret = {'comment': 'Failed to delete job {0} from schedule.'.format(name),
'result': False}
if not name:
ret['comment'] = 'Job name is required.'
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be deleted from schedule.'.format(name)
ret['result'] = True
else:
persist = kwargs.get('persist', True)
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name, 'func': 'delete', 'persist': persist}
elif name in list_(show_all=True, where='pillar', return_yaml=False):
event_data = {'name': name, 'where': 'pillar', 'func': 'delete', 'persist': False}
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
return ret
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_delete_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
if name not in schedule:
ret['result'] = True
ret['comment'] = 'Deleted Job {0} from schedule.'.format(name)
else:
ret['comment'] = 'Failed to delete job {0} from schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule add failed.'
return ret
def build_schedule_item(name, **kwargs):
'''
Build a schedule job
CLI Example:
.. code-block:: bash
salt '*' schedule.build_schedule_item job1 function='test.ping' seconds=3600
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
return ret
schedule = {}
schedule[name] = salt.utils.odict.OrderedDict()
schedule[name]['function'] = kwargs['function']
time_conflict = False
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs and 'when' in kwargs:
time_conflict = True
if item in kwargs and 'cron' in kwargs:
time_conflict = True
if time_conflict:
ret['result'] = False
ret['comment'] = 'Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.'
return ret
if 'when' in kwargs and 'cron' in kwargs:
ret['result'] = False
ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.'
return ret
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs:
schedule[name][item] = kwargs[item]
if 'return_job' in kwargs:
schedule[name]['return_job'] = kwargs['return_job']
if 'metadata' in kwargs:
schedule[name]['metadata'] = kwargs['metadata']
if 'job_args' in kwargs:
schedule[name]['args'] = kwargs['job_args']
if 'job_kwargs' in kwargs:
schedule[name]['kwargs'] = kwargs['job_kwargs']
if 'maxrunning' in kwargs:
schedule[name]['maxrunning'] = kwargs['maxrunning']
else:
schedule[name]['maxrunning'] = 1
if 'name' in kwargs:
schedule[name]['name'] = kwargs['name']
else:
schedule[name]['name'] = name
if 'enabled' in kwargs:
schedule[name]['enabled'] = kwargs['enabled']
else:
schedule[name]['enabled'] = True
if 'jid_include' not in kwargs or kwargs['jid_include']:
schedule[name]['jid_include'] = True
if 'splay' in kwargs:
if isinstance(kwargs['splay'], dict):
# Ensure ordering of start and end arguments
schedule[name]['splay'] = salt.utils.odict.OrderedDict()
schedule[name]['splay']['start'] = kwargs['splay']['start']
schedule[name]['splay']['end'] = kwargs['splay']['end']
else:
schedule[name]['splay'] = kwargs['splay']
if 'when' in kwargs:
if not _WHEN_SUPPORTED:
ret['result'] = False
ret['comment'] = 'Missing dateutil.parser, "when" is unavailable.'
return ret
else:
validate_when = kwargs['when']
if not isinstance(validate_when, list):
validate_when = [validate_when]
for _when in validate_when:
try:
dateutil_parser.parse(_when)
except ValueError:
ret['result'] = False
ret['comment'] = 'Schedule item {0} for "when" in invalid.'.format(_when)
return ret
for item in ['range', 'when', 'once', 'once_fmt', 'cron',
'returner', 'after', 'return_config', 'return_kwargs',
'until', 'run_on_start', 'skip_during_range']:
if item in kwargs:
schedule[name][item] = kwargs[item]
return schedule[name]
def add(name, **kwargs):
'''
Add a job to the schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.add job1 function='test.ping' seconds=3600
# If function have some arguments, use job_args
salt '*' schedule.add job2 function='cmd.run' job_args="['date >> /tmp/date.log']" seconds=60
'''
ret = {'comment': 'Failed to add job {0} to schedule.'.format(name),
'result': False}
if name in list_(show_all=True, return_yaml=False):
ret['comment'] = 'Job {0} already exists in schedule.'.format(name)
ret['result'] = False
return ret
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
time_conflict = False
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs and 'when' in kwargs:
time_conflict = True
if item in kwargs and 'cron' in kwargs:
time_conflict = True
if time_conflict:
ret['comment'] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.'
return ret
if 'when' in kwargs and 'cron' in kwargs:
ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.'
return ret
persist = kwargs.get('persist', True)
_new = build_schedule_item(name, **kwargs)
if 'result' in _new and not _new['result']:
return _new
schedule_data = {}
schedule_data[name] = _new
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be added to schedule.'.format(name)
ret['result'] = True
else:
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'name': name,
'schedule': schedule_data,
'func': 'add',
'persist': persist}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_add_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
if name in schedule:
ret['result'] = True
ret['comment'] = 'Added job: {0} to schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule add failed.'
return ret
def modify(name, **kwargs):
'''
Modify an existing job in the schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.modify job1 function='test.ping' seconds=3600
'''
ret = {'comment': '',
'changes': {},
'result': True}
time_conflict = False
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs and 'when' in kwargs:
time_conflict = True
if item in kwargs and 'cron' in kwargs:
time_conflict = True
if time_conflict:
ret['result'] = False
ret['comment'] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" option.'
return ret
if 'when' in kwargs and 'cron' in kwargs:
ret['result'] = False
ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.'
return ret
current_schedule = list_(show_all=True, return_yaml=False)
if name not in current_schedule:
ret['comment'] = 'Job {0} does not exist in schedule.'.format(name)
ret['result'] = False
return ret
_current = current_schedule[name]
if 'function' not in kwargs:
kwargs['function'] = _current.get('function')
if '_seconds' in _current:
_current['seconds'] = _current['_seconds']
del _current['_seconds']
_new = build_schedule_item(name, **kwargs)
if 'result' in _new and not _new['result']:
return _new
if _new == _current:
ret['comment'] = 'Job {0} in correct state'.format(name)
return ret
_current_lines = ['{0}:{1}\n'.format(key, value)
for (key, value) in sorted(_current.items())]
_new_lines = ['{0}:{1}\n'.format(key, value)
for (key, value) in sorted(_new.items())]
_diff = difflib.unified_diff(_current_lines, _new_lines)
ret['changes']['diff'] = ''.join(_diff)
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be modified in schedule.'.format(name)
else:
persist = kwargs.get('persist', True)
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name,
'schedule': _new,
'func': 'modify',
'persist': persist}
elif name in list_(show_all=True, where='pillar', return_yaml=False):
event_data = {'name': name,
'schedule': _new,
'where': 'pillar',
'func': 'modify',
'persist': False}
out = __salt__['event.fire'](event_data, 'manage_schedule')
if out:
ret['comment'] = 'Modified job: {0} in schedule.'.format(name)
else:
ret['comment'] = 'Failed to modify job {0} in schedule.'.format(name)
ret['result'] = False
return ret
def run_job(name, force=False):
'''
Run a scheduled job on the minion immediately
CLI Example:
.. code-block:: bash
salt '*' schedule.run_job job1
salt '*' schedule.run_job job1 force=True
Force the job to run even if it is disabled.
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
schedule = list_(show_all=True, return_yaml=False)
if name in schedule:
data = schedule[name]
if 'enabled' in data and not data['enabled'] and not force:
ret['comment'] = 'Job {0} is disabled.'.format(name)
else:
out = __salt__['event.fire']({'name': name, 'func': 'run_job'}, 'manage_schedule')
if out:
ret['comment'] = 'Scheduling Job {0} on minion.'.format(name)
else:
ret['comment'] = 'Failed to run job {0} on minion.'.format(name)
ret['result'] = False
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
def enable_job(name, **kwargs):
'''
Enable a job in the minion's schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.enable_job job1
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if 'test' in __opts__ and __opts__['test']:
ret['comment'] = 'Job: {0} would be enabled in schedule.'.format(name)
else:
persist = kwargs.get('persist', True)
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name, 'func': 'enable_job', 'persist': persist}
elif name in list_(show_all=True, where='pillar', return_yaml=False):
event_data = {'name': name, 'where': 'pillar', 'func': 'enable_job', 'persist': False}
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_enabled_job_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
# check item exists in schedule and is enabled
if name in schedule and schedule[name]['enabled']:
ret['result'] = True
ret['comment'] = 'Enabled Job {0} in schedule.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to enable job {0} in schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule enable job failed.'
return ret
def disable_job(name, **kwargs):
'''
Disable a job in the minion's schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.disable_job job1
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be disabled in schedule.'.format(name)
else:
persist = kwargs.get('persist', True)
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name, 'func': 'disable_job', 'persist': persist}
elif name in list_(show_all=True, where='pillar'):
event_data = {'name': name, 'where': 'pillar', 'func': 'disable_job', 'persist': False}
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_disabled_job_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
# check item exists in schedule and is enabled
if name in schedule and not schedule[name]['enabled']:
ret['result'] = True
ret['comment'] = 'Disabled Job {0} in schedule.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to disable job {0} in schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule enable job failed.'
return ret
def save(**kwargs):
'''
Save all scheduled jobs on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.save
'''
ret = {'comment': [],
'result': True}
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Schedule would be saved.'
else:
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'func': 'save_schedule'}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_saved',
wait=30,
)
if event_ret and event_ret['complete']:
ret['result'] = True
ret['comment'] = 'Schedule (non-pillar items) saved.'
else:
ret['result'] = False
ret['comment'] = 'Failed to save schedule.'
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule save failed.'
return ret
def enable(**kwargs):
'''
Enable all scheduled jobs on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.enable
'''
ret = {'comment': [],
'result': True}
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Schedule would be enabled.'
else:
persist = kwargs.get('persist', True)
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'func': 'enable', 'persist': persist}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_enabled_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
if 'enabled' in schedule and schedule['enabled']:
ret['result'] = True
ret['comment'] = 'Enabled schedule on minion.'
else:
ret['result'] = False
ret['comment'] = 'Failed to enable schedule on minion.'
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule enable job failed.'
return ret
def disable(**kwargs):
'''
Disable all scheduled jobs on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.disable
'''
ret = {'comment': [],
'result': True}
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Schedule would be disabled.'
else:
persist = kwargs.get('persist', True)
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire']({'func': 'disable', 'persist': persist}, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_disabled_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
if 'enabled' in schedule and not schedule['enabled']:
ret['result'] = True
ret['comment'] = 'Disabled schedule on minion.'
else:
ret['result'] = False
ret['comment'] = 'Failed to disable schedule on minion.'
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule disable job failed.'
return ret
def reload_():
'''
Reload saved scheduled jobs on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.reload
'''
ret = {'comment': [],
'result': True}
# If there a schedule defined in pillar, refresh it.
if 'schedule' in __pillar__:
out = __salt__['event.fire']({}, 'pillar_refresh')
if out:
ret['comment'].append('Reloaded schedule from pillar on minion.')
else:
ret['comment'].append('Failed to reload schedule from pillar on minion.')
ret['result'] = False
# move this file into an configurable opt
sfn = '{0}/{1}/schedule.conf'.format(__opts__['config_dir'], os.path.dirname(__opts__['default_include']))
if os.path.isfile(sfn):
with salt.utils.files.fopen(sfn, 'rb') as fp_:
try:
schedule = salt.utils.yaml.safe_load(fp_)
except salt.utils.yaml.YAMLError as exc:
ret['comment'].append('Unable to read existing schedule file: {0}'.format(exc))
if schedule:
if 'schedule' in schedule and schedule['schedule']:
out = __salt__['event.fire']({'func': 'reload', 'schedule': schedule}, 'manage_schedule')
if out:
ret['comment'].append('Reloaded schedule on minion from schedule.conf.')
else:
ret['comment'].append('Failed to reload schedule on minion from schedule.conf.')
ret['result'] = False
else:
ret['comment'].append('Failed to reload schedule on minion. Saved file is empty or invalid.')
ret['result'] = False
else:
ret['comment'].append('Failed to reload schedule on minion. Saved file is empty or invalid.')
ret['result'] = False
return ret
def move(name, target, **kwargs):
'''
Move scheduled job to another minion or minions.
CLI Example:
.. code-block:: bash
salt '*' schedule.move jobname target
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be moved from schedule.'.format(name)
else:
opts_schedule = list_(show_all=True, where='opts', return_yaml=False)
pillar_schedule = list_(show_all=True, where='pillar', return_yaml=False)
if name in opts_schedule:
schedule_data = opts_schedule[name]
where = None
elif name in pillar_schedule:
schedule_data = pillar_schedule[name]
where = 'pillar'
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
schedule_opts = []
for key, value in six.iteritems(schedule_data):
temp = '{0}={1}'.format(key, value)
schedule_opts.append(temp)
response = __salt__['publish.publish'](target, 'schedule.add', schedule_opts)
# Get errors and list of affeced minions
errors = []
minions = []
for minion in response:
minions.append(minion)
if not response[minion]:
errors.append(minion)
# parse response
if not response:
ret['comment'] = 'no servers answered the published schedule.add command'
return ret
elif len(errors) > 0:
ret['comment'] = 'the following minions return False'
ret['minions'] = errors
return ret
else:
delete(name, where=where)
ret['result'] = True
ret['comment'] = 'Moved Job {0} from schedule.'.format(name)
ret['minions'] = minions
return ret
return ret
def copy(name, target, **kwargs):
'''
Copy scheduled job to another minion or minions.
CLI Example:
.. code-block:: bash
salt '*' schedule.copy jobname target
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if 'test' in kwargs and kwargs['test']:
ret['comment'] = 'Job: {0} would be copied from schedule.'.format(name)
else:
opts_schedule = list_(show_all=True, where='opts', return_yaml=False)
pillar_schedule = list_(show_all=True, where='pillar', return_yaml=False)
if name in opts_schedule:
schedule_data = opts_schedule[name]
elif name in pillar_schedule:
schedule_data = pillar_schedule[name]
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
schedule_opts = []
for key, value in six.iteritems(schedule_data):
temp = '{0}={1}'.format(key, value)
schedule_opts.append(temp)
response = __salt__['publish.publish'](target, 'schedule.add', schedule_opts)
# Get errors and list of affeced minions
errors = []
minions = []
for minion in response:
minions.append(minion)
if not response[minion]:
errors.append(minion)
# parse response
if not response:
ret['comment'] = 'no servers answered the published schedule.add command'
return ret
elif len(errors) > 0:
ret['comment'] = 'the following minions return False'
ret['minions'] = errors
return ret
else:
ret['result'] = True
ret['comment'] = 'Copied Job {0} from schedule to minion(s).'.format(name)
ret['minions'] = minions
return ret
return ret
def postpone_job(name,
current_time,
new_time,
**kwargs):
'''
Postpone a job in the minion's schedule
Current time and new time should be in date string format,
default value is %Y-%m-%dT%H:%M:%S.
.. versionadded:: 2018.3.0
CLI Example:
.. code-block:: bash
salt '*' schedule.postpone_job job current_time new_time
salt '*' schedule.postpone_job job current_time new_time time_fmt='%Y-%m-%dT%H:%M:%S'
'''
time_fmt = kwargs.get('time_fmt') or '%Y-%m-%dT%H:%M:%S'
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
return ret
if not current_time:
ret['comment'] = 'Job current time is required.'
ret['result'] = False
return ret
else:
try:
# Validate date string
datetime.datetime.strptime(current_time, time_fmt)
except (TypeError, ValueError):
log.error('Date string could not be parsed: %s, %s',
new_time, time_fmt)
ret['comment'] = 'Date string could not be parsed.'
ret['result'] = False
return ret
if not new_time:
ret['comment'] = 'Job new_time is required.'
ret['result'] = False
return ret
else:
try:
# Validate date string
datetime.datetime.strptime(new_time, time_fmt)
except (TypeError, ValueError):
log.error('Date string could not be parsed: %s, %s',
new_time, time_fmt)
ret['comment'] = 'Date string could not be parsed.'
ret['result'] = False
return ret
if 'test' in __opts__ and __opts__['test']:
ret['comment'] = 'Job: {0} would be postponed in schedule.'.format(name)
else:
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name,
'time': current_time,
'new_time': new_time,
'time_fmt': time_fmt,
'func': 'postpone_job'}
elif name in list_(show_all=True, where='pillar', return_yaml=False):
event_data = {'name': name,
'time': current_time,
'new_time': new_time,
'time_fmt': time_fmt,
'where': 'pillar',
'func': 'postpone_job'}
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_postpone_job_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
# check item exists in schedule and is enabled
if name in schedule and schedule[name]['enabled']:
ret['result'] = True
ret['comment'] = 'Postponed Job {0} in schedule.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to postpone job {0} in schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule postpone job failed.'
return ret
def skip_job(name, current_time, **kwargs):
'''
Skip a job in the minion's schedule at specified time.
Time to skip should be specified as date string format,
default value is %Y-%m-%dT%H:%M:%S.
.. versionadded:: 2018.3.0
CLI Example:
.. code-block:: bash
salt '*' schedule.skip_job job time
'''
time_fmt = kwargs.get('time_fmt') or '%Y-%m-%dT%H:%M:%S'
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if not current_time:
ret['comment'] = 'Job time is required.'
ret['result'] = False
else:
# Validate date string
try:
datetime.datetime.strptime(current_time, time_fmt)
except (TypeError, ValueError):
log.error('Date string could not be parsed: %s, %s',
current_time, time_fmt)
ret['comment'] = 'Date string could not be parsed.'
ret['result'] = False
return ret
if 'test' in __opts__ and __opts__['test']:
ret['comment'] = 'Job: {0} would be skipped in schedule.'.format(name)
else:
if name in list_(show_all=True, where='opts', return_yaml=False):
event_data = {'name': name,
'time': current_time,
'time_fmt': time_fmt,
'func': 'skip_job'}
elif name in list_(show_all=True, where='pillar', return_yaml=False):
event_data = {'name': name,
'time': current_time,
'time_fmt': time_fmt,
'where': 'pillar',
'func': 'skip_job'}
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
try:
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data, 'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_skip_job_complete',
wait=30,
)
if event_ret and event_ret['complete']:
schedule = event_ret['schedule']
# check item exists in schedule and is enabled
if name in schedule and schedule[name]['enabled']:
ret['result'] = True
ret['comment'] = 'Added Skip Job {0} in schedule.'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Failed to skip job {0} in schedule.'.format(name)
return ret
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret['comment'] = 'Event module not available. Schedule skip job failed.'
return ret
def show_next_fire_time(name, **kwargs):
'''
Show the next fire time for scheduled job
.. versionadded:: 2018.3.0
CLI Example:
.. code-block:: bash
salt '*' schedule.show_next_fire_time job_name
'''
ret = {'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
try:
event_data = {'name': name, 'func': 'get_next_fire_time'}
with salt.utils.event.get_event('minion', opts=__opts__) as event_bus:
res = __salt__['event.fire'](event_data,
'manage_schedule')
if res:
event_ret = event_bus.get_event(
tag='/salt/minion/minion_schedule_next_fire_time_complete',
wait=30,
)
except KeyError:
# Effectively a no-op, since we can't really return without an event system
ret = {}
ret['comment'] = 'Event module not available. Schedule show next fire time failed.'
ret['result'] = True
return ret
if 'next_fire_time' in event_ret:
ret['next_fire_time'] = event_ret['next_fire_time']
else:
ret['comment'] = 'next fire time not available.'
return ret