Merge remote-tracking branch 'upstream/develop' into sam_raet_43

This commit is contained in:
Samuel M Smith 2014-06-05 14:08:09 -06:00
commit bb925bee2b
17 changed files with 494 additions and 87 deletions

View File

@ -5,9 +5,10 @@ involves preparing the three listeners and the workers needed by the master.
'''
# Import python libs
import fnmatch
import logging
import os
import re
import logging
import time
try:
import pwd
@ -76,7 +77,7 @@ def clean_fsbackend(opts):
'''
Clean out the old fileserver backends
'''
# Clear remote fileserver backend env cache so it gets recreated
# Clear remote fileserver backend caches so they get recreated
for backend in ('git', 'hg', 'svn'):
if backend in opts['fileserver_backend']:
env_cache = os.path.join(
@ -94,6 +95,25 @@ def clean_fsbackend(opts):
.format(env_cache, exc)
)
file_lists_dir = os.path.join(
opts['cachedir'],
'file_lists',
'{0}fs'.format(backend)
)
try:
file_lists_caches = os.listdir(file_lists_dir)
except OSError:
continue
for file_lists_cache in fnmatch.filter(file_lists_caches, '*.p'):
cache_file = os.path.join(file_lists_dir, file_lists_cache)
try:
os.remove(cache_file)
except (IOError, OSError) as exc:
log.critical(
'Unable to file_lists cache file {0}: {1}'
.format(cache_file, exc)
)
def clean_expired_tokens(opts):
'''
@ -704,7 +724,7 @@ class RemoteFuncs(object):
if 'jid' in minion:
ret['__jid__'] = minion['jid']
for key, val in self.local.get_cache_returns(ret['__jid__']).items():
if not key in ret:
if key not in ret:
ret[key] = val
if load.get('form', '') != 'full':
ret.pop('__jid__')

View File

@ -590,6 +590,9 @@ def init():
'hash': repo_hash,
'cachedir': rp_
})
# Strip trailing slashes from the gitfs root as these cause
# path searches to fail.
repo_conf['root'] = repo_conf['root'].rstrip(os.path.sep)
repos.append(repo_conf)
except Exception as exc:

View File

@ -1254,6 +1254,25 @@ class Minion(MinionBase):
).compile_pillar()
self.module_refresh()
def manage_schedule(self, package):
'''
Refresh the functions and returners.
'''
tag, data = salt.utils.event.MinionEvent.unpack(package)
func = data.get('func', None)
if func == 'delete':
job = data.get('job', None)
self.schedule.delete_job(job)
elif func == 'add':
name = data.get('name', None)
schedule = data.get('schedule', None)
self.schedule.add_job(name, schedule)
elif func == 'modify':
name = data.get('name', None)
schedule = data.get('schedule', None)
self.schedule.modify_job(name, schedule)
def environ_setenv(self, package):
'''
Set the salt-minion main process environment according to
@ -1400,6 +1419,8 @@ class Minion(MinionBase):
self.module_refresh()
elif package.startswith('pillar_refresh'):
self.pillar_refresh()
elif package.startswith('manage_schedule'):
self.manage_schedule(package)
elif package.startswith('grains_refresh'):
if self.grains_cache != self.opts['grains']:
self.pillar_refresh()

View File

@ -129,7 +129,7 @@ def available():
for root, dirs, files in os.walk(mod_dir):
for fn_ in files:
if '.ko' in fn_:
ret.append(fn_[:fn_.index('.ko')])
ret.append(fn_[:fn_.index('.ko')].replace('-', '_'))
return sorted(list(ret))

289
salt/modules/schedule.py Normal file
View File

@ -0,0 +1,289 @@
# -*- coding: utf-8 -*-
'''
Module for manging the Salt schedule on a minion
.. versionadded:: Helium
'''
# Import Python libs
import os
import yaml
import salt.utils
__proxyenabled__ = ['*']
import logging
log = logging.getLogger(__name__)
SCHEDULE_CONF = [
'function',
'splay',
'range',
'when',
'returner',
'jid_include',
'args',
'kwargs',
'_seconds',
'seconds',
'minutes',
'hours',
'days'
]
def list():
'''
List the jobs currently scheduled on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.list
'''
schedule = __opts__['schedule']
for job in schedule.keys():
if job.startswith('_'):
del schedule[job]
continue
for item in schedule[job].keys():
if not item in SCHEDULE_CONF:
del schedule[job][item]
continue
if schedule[job][item] == 'true':
schedule[job][item] = True
if schedule[job][item] == 'false':
schedule[job][item] = False
if '_seconds' in schedule[job].keys():
schedule[job]['seconds'] = schedule[job]['_seconds']
del schedule[job]['_seconds']
if schedule:
tmp = {'schedule': schedule}
yaml_out = yaml.safe_dump(tmp, default_flow_style=False)
return yaml_out
else:
return None
def purge():
'''
Purge all the jobs currently scheduled on the minion
CLI Example:
.. code-block:: bash
salt '*' schedule.purge
'''
ret = {'comment': [],
'result': True}
schedule = __opts__['schedule']
for job in schedule.keys():
if job.startswith('_'):
continue
out = __salt__['event.fire']({'job': job, 'func': 'delete'}, 'manage_schedule')
if out:
ret['comment'].append('Deleted job: {0} from schedule.'.format(job))
else:
ret['comment'].append('Failed to delete job {0} from schedule.'.format(job))
ret['result'] = False
return ret
def delete(name):
'''
Delete a job from the minion's schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.delete job1
'''
ret = {'comment': [],
'result': True}
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
if name in __opts__['schedule']:
out = __salt__['event.fire']({'job': name, 'func': 'delete'}, 'manage_schedule')
if out:
ret['comment'] = 'Deleted Job {0} from schedule.'.format(name)
else:
ret['comment'] = 'Failed to delete job {0} from schedule.'.format(name)
ret['result'] = False
else:
ret['comment'] = 'Job {0} does not exist.'.format(name)
ret['result'] = False
return ret
def add(name, **kwargs):
'''
Add a job to the schedule
CLI Example:
.. code-block:: bash
salt '*' schedule.add job1 function='test.ping' seconds=3600
'''
ret = {'comment': [],
'result': True}
if name in __opts__['schedule']:
ret['comment'] = 'Job {0} already exists in schedule.'.format(name)
ret['result'] = True
return ret
if not name:
ret['comment'] = 'Job name is required.'
ret['result'] = False
schedule = {'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 time_conflict:
return 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" option.'
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs:
schedule[item] = kwargs[item]
if 'job_args' in kwargs:
schedule['args'] = kwargs['job_args']
if 'job_kwargs' in kwargs:
schedule['kwargs'] = kwargs['job_kwargs']
for item in ['splay', 'range', 'when', 'returner', 'jid_include']:
if item in kwargs:
schedule[item] = kwargs[item]
out = __salt__['event.fire']({'name': name, 'schedule': schedule, 'func': 'add'}, 'manage_schedule')
if out:
ret['comment'] = 'Added job: {0} to schedule.'.format(name)
else:
ret['comment'] = 'Failed to modify job {0} to schedule.'.format(name)
ret['result'] = False
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': [],
'result': True}
if not name in __opts__['schedule']:
ret['comment'] = 'Job {0} does not exist in schedule.'.format(name)
ret['result'] = False
return ret
schedule = {'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 time_conflict:
return 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" option.'
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in kwargs:
schedule[item] = kwargs[item]
if 'job_args' in kwargs:
schedule['args'] = kwargs['job_args']
if 'job_kwargs' in kwargs:
schedule['kwargs'] = kwargs['job_kwargs']
for item in ['splay', 'range', 'when', 'returner', 'jid_include']:
if item in kwargs:
schedule[item] = kwargs[item]
out = __salt__['event.fire']({'name': name, 'schedule': schedule, 'func': 'modify'}, '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 save():
'''
CLI Example:
.. code-block:: bash
salt '*' schedule.save
'''
ret = {'comment': [],
'result': True}
schedule = __opts__['schedule']
for job in schedule.keys():
if job.startswith('_'):
del schedule[job]
continue
for item in schedule[job].keys():
if not item in SCHEDULE_CONF:
del schedule[job][item]
continue
if schedule[job][item] == 'true':
schedule[job][item] = True
if schedule[job][item] == 'false':
schedule[job][item] = False
if '_seconds' in schedule[job].keys():
schedule[job]['seconds'] = schedule[job]['_seconds']
del schedule[job]['_seconds']
# move this file into an configurable opt
sfn = '{0}/{1}/schedule.conf'.format(__opts__['config_dir'], os.path.dirname(__opts__['default_include']))
if schedule:
tmp = {'schedule': schedule}
yaml_out = yaml.safe_dump(tmp, default_flow_style=False)
else:
yaml_out = ''
try:
with salt.utils.fopen(sfn, 'w+') as fp_:
fp_.write(yaml_out)
ret['comment'] = 'Schedule saved to {0}.'.format(sfn)
except (IOError, OSError):
ret['comment'] = 'Unable to write to schedule file at {0}. Check permissions.'.format(sfn)
ret['result'] = False
return ret

View File

@ -169,6 +169,22 @@ def gen_password(password, crypt_salt=None, algorithm='sha512'):
return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm)
def del_password(name):
'''
Delete the password from name user
CLI Example:
.. code-block:: bash
salt '*' shadow.del_password username
'''
cmd = 'passwd -d {0}'.format(name)
__salt__['cmd.run'](cmd, output_loglevel='quiet')
uinfo = info(name)
return not uinfo['passwd']
def set_password(name, password, use_usermod=False):
'''
Set the password for a named user. The password must be a properly defined

View File

@ -130,7 +130,7 @@ def _get_repo_options(**kwargs):
repo_arg = ''
if fromrepo:
log.info('Restricting to repo {0!r}'.format(fromrepo))
repo_arg = ('--disablerepo={0!r} --enablerepo={1!r}'
repo_arg = ('--disablerepo={0!r} --enablerepo={1!r} '
.format('*', fromrepo))
else:
repo_arg = ''
@ -726,6 +726,10 @@ def install(name=None,
Disable exclude from main, for a repo or for everything.
(e.g., ``yum --disableexcludes='main'``)
branch
Specifies the branch on YUM server.
(e.g., ``yum --branch='test'``)
.. versionadded:: Helium
@ -785,6 +789,10 @@ def install(name=None,
'package targets')
repo_arg = _get_repo_options(fromrepo=fromrepo, **kwargs)
# Support branch parameter for yum
branch = kwargs.get('branch', '')
if branch:
repo_arg += '--branch={0!r}'.format(branch)
exclude_arg = _get_excludes_option(**kwargs)
old = list_pkgs()

View File

@ -55,6 +55,7 @@ def _changes(name,
createhome=True,
password=None,
enforce_password=True,
empty_password=False,
shell=None,
fullname='',
roomnumber='',
@ -160,6 +161,7 @@ def present(name,
createhome=True,
password=None,
enforce_password=True,
empty_password=False,
shell=None,
unique=True,
system=False,
@ -229,6 +231,9 @@ def present(name,
"password" field. This option will be ignored if "password" is not
specified.
empty_password
Set to True to enable no password-less login for user
shell
The login shell, defaults to the system default shell
@ -325,6 +330,9 @@ def present(name,
if gid_from_name:
gid = __salt__['file.group_to_gid'](name)
if empty_password:
__salt__['shadow.del_password'](name)
changes = _changes(name,
uid,
gid,
@ -335,6 +343,7 @@ def present(name,
createhome,
password,
enforce_password,
empty_password,
shell,
fullname,
roomnumber,
@ -360,7 +369,7 @@ def present(name,
lshad = __salt__['shadow.info'](name)
pre = __salt__['user.info'](name)
for key, val in changes.items():
if key == 'passwd':
if key == 'passwd' and not empty_password:
__salt__['shadow.set_password'](name, password)
continue
if key == 'date':
@ -419,6 +428,7 @@ def present(name,
createhome,
password,
enforce_password,
empty_password,
shell,
fullname,
roomnumber,
@ -464,7 +474,7 @@ def present(name,
ret['comment'] = 'New user {0} created'.format(name)
ret['changes'] = __salt__['user.info'](name)
if 'shadow.info' in __salt__ and not salt.utils.is_windows():
if password:
if password and not empty_password:
__salt__['shadow.set_password'](name, password)
spost = __salt__['shadow.info'](name)
if spost['passwd'] != password:

View File

@ -189,6 +189,23 @@ class Schedule(object):
return self.functions['config.merge'](opt, {}, omit_master=True)
return self.opts.get(opt, {})
def delete_job(self, name):
# ensure job exists, then delete it
if name in self.opts['schedule']:
del self.opts['schedule'][name]
# remove from self.intervals
if name in self.intervals:
del self.intervals[name]
def add_job(self, name, schedule):
self.opts['schedule'][name] = schedule
def modify_job(self, name, schedule):
if name in self.opts['schedule']:
self.delete_job(name)
self.opts['schedule'][name] = schedule
def handle_func(self, func, data):
'''
Execute this method in a multiprocess or thread
@ -311,6 +328,7 @@ class Schedule(object):
Evaluate and execute the schedule
'''
schedule = self.option('schedule')
#log.debug('calling eval {0}'.format(schedule))
if not isinstance(schedule, dict):
return
for job, data in schedule.items():
@ -335,8 +353,12 @@ class Schedule(object):
when = 0
seconds = 0
# clean this up
if ('seconds' in data or 'hours' in data or 'minutes' in data or 'days' in data) and 'when' in data:
time_conflict = False
for item in ['seconds', 'minutes', 'hours', 'days']:
if item in data and 'when' in data:
time_conflict = True
if time_conflict:
log.info('Unable to use "seconds", "minutes", "hours", or "days" with "when" option. Ignoring.')
continue
@ -440,6 +462,7 @@ class Schedule(object):
else:
if now - self.intervals[job] >= seconds:
run = True
else:
if 'splay' in data:
if 'when' in data:

View File

@ -282,7 +282,7 @@ class TestDaemon(object):
self.pre_setup_minions()
self.setup_minions()
if self.parser.options.ssh:
if getattr(self.parser.options, 'ssh', False):
self.prep_ssh()
if self.parser.options.sysinfo:

View File

@ -7,7 +7,7 @@
from salttesting.unit import skipIf
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON
ensure_in_syspath('../')
ensure_in_syspath('../..')
# Import salt libs
import integration

View File

@ -8,7 +8,7 @@ from salttesting import skipIf
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import patch, NO_MOCK, NO_MOCK_REASON
ensure_in_syspath('../')
ensure_in_syspath('../..')
# Import Python libs
import os
@ -23,6 +23,10 @@ gitfs.__opts__ = {'gitfs_remotes': [''],
'fileserver_backend': 'gitfs',
'gitfs_base': 'master',
'fileserver_events': True,
'transport': 'zeromq',
'gitfs_mountpoint': '',
'gitfs_env_whitelist': [],
'gitfs_env_blacklist': []
}
load = {'saltenv': 'base'}
@ -88,7 +92,11 @@ class GitFSTest(integration.ModuleCase):
'gitfs_remotes': ['file://' + self.tmp_repo_git],
'sock_dir': self.master_opts['sock_dir']}):
ret = gitfs.find_file('testfile')
expected_ret = {'path': '/tmp/salttest/cache/gitfs/refs/master/testfile',
expected_ret = {'path': os.path.join(self.master_opts['cachedir'],
'gitfs',
'refs',
'base',
'testfile'),
'rel': 'testfile'}
self.assertDictEqual(ret, expected_ret)
@ -140,8 +148,18 @@ class GitFSTest(integration.ModuleCase):
ret = gitfs.serve_file(load, fnd)
self.assertDictEqual({
'data': 'Scene 24\n\n \n OLD MAN: Ah, hee he he ha!\n ARTHUR: And this enchanter of whom you speak, he has seen the grail?\n OLD MAN: Ha ha he he he he!\n ARTHUR: Where does he live? Old man, where does he live?\n OLD MAN: He knows of a cave, a cave which no man has entered.\n ARTHUR: And the Grail... The Grail is there?\n OLD MAN: Very much danger, for beyond the cave lies the Gorge\n of Eternal Peril, which no man has ever crossed.\n ARTHUR: But the Grail! Where is the Grail!?\n OLD MAN: Seek you the Bridge of Death.\n ARTHUR: The Bridge of Death, which leads to the Grail?\n OLD MAN: Hee hee ha ha!\n\n',
'dest': 'testfile'}, ret)
'data': 'Scene 24\n\n \n OLD MAN: Ah, hee he he ha!\n ARTHUR: '
'And this enchanter of whom you speak, he has seen the grail?\n '
'OLD MAN: Ha ha he he he he!\n ARTHUR: Where does he live? '
'Old man, where does he live?\n OLD MAN: He knows of a cave, '
'a cave which no man has entered.\n ARTHUR: And the Grail... '
'The Grail is there?\n OLD MAN: Very much danger, for beyond '
'the cave lies the Gorge\n of Eternal Peril, which no man '
'has ever crossed.\n ARTHUR: But the Grail! Where is the Grail!?\n '
'OLD MAN: Seek you the Bridge of Death.\n ARTHUR: The Bridge of '
'Death, which leads to the Grail?\n OLD MAN: Hee hee ha ha!\n\n',
'dest': 'testfile'},
ret)
if __name__ == '__main__':

View File

@ -7,7 +7,7 @@
from salttesting import skipIf
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import patch, NO_MOCK, NO_MOCK_REASON
ensure_in_syspath('../')
ensure_in_syspath('../..')
# Import salt libs
import integration
@ -22,11 +22,15 @@ import os
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RootsTest(integration.ModuleCase):
def setUp(self):
self.master_opts['file_roots']['base'] = [os.path.join(integration.FILES, 'file', 'base')]
if integration.TMP_STATE_TREE not in self.master_opts['file_roots']['base']:
# We need to setup the file roots
self.master_opts['file_roots']['base'] = [os.path.join(integration.FILES, 'file', 'base')]
def test_file_list(self):
with patch.dict(roots.__opts__, {'file_roots': self.master_opts['file_roots'],
with patch.dict(roots.__opts__, {'cachedir': self.master_opts['cachedir'],
'file_roots': self.master_opts['file_roots'],
'fileserver_ignoresymlinks': False,
'fileserver_followsymlinks': False,
'file_ignore_regex': False,
@ -102,7 +106,10 @@ class RootsTest(integration.ModuleCase):
self.assertDictEqual(ret, {'hsum': '98aa509006628302ce38ce521a7f805f', 'hash_type': 'md5'})
def test_file_list_emptydirs(self):
with patch.dict(roots.__opts__, {'file_roots': self.master_opts['file_roots'],
if integration.TMP_STATE_TREE not in self.master_opts['file_roots']['base']:
self.skipTest('This test fails when using tests/runtests.py. salt-runtests will be available soon.')
with patch.dict(roots.__opts__, {'cachedir': self.master_opts['cachedir'],
'file_roots': self.master_opts['file_roots'],
'fileserver_ignoresymlinks': False,
'fileserver_followsymlinks': False,
'file_ignore_regex': False,
@ -111,11 +118,14 @@ class RootsTest(integration.ModuleCase):
self.assertIn('empty_dir', ret)
def test_dir_list(self):
with patch.dict(roots.__opts__, {'file_roots': self.master_opts['file_roots'],
'fileserver_ignoresymlinks': False,
'fileserver_followsymlinks': False,
'file_ignore_regex': False,
'file_ignore_glob': False}):
if integration.TMP_STATE_TREE not in self.master_opts['file_roots']['base']:
self.skipTest('This test fails when using tests/runtests.py. salt-runtests will be available soon.')
with patch.dict(roots.__opts__, {'cachedir': self.master_opts['cachedir'],
'file_roots': self.master_opts['file_roots'],
'fileserver_ignoresymlinks': False,
'fileserver_followsymlinks': False,
'file_ignore_regex': False,
'file_ignore_glob': False}):
ret = roots.dir_list({'saltenv': 'base'})
self.assertIn('empty_dir', ret)

View File

@ -85,34 +85,8 @@ class CallTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
@skipIf(sys.platform.startswith('win'), 'This test does not apply on Win')
def test_return(self):
config_dir = '/tmp/salttest'
minion_config_file = os.path.join(config_dir, 'minion')
minion_config = {
'id': 'minion_test_issue_2731',
'master': 'localhost',
'master_port': 64506,
'root_dir': '/tmp/salttest',
'pki_dir': 'pki',
'cachedir': 'cachedir',
'sock_dir': 'minion_sock',
'open_mode': True,
'log_file': '/tmp/salttest/minion_test_issue_2731',
'log_level': 'quiet',
'log_level_logfile': 'info'
}
# Remove existing logfile
if os.path.isfile('/tmp/salttest/minion_test_issue_2731'):
os.unlink('/tmp/salttest/minion_test_issue_2731')
# Let's first test with a master running
open(minion_config_file, 'w').write(
yaml.dump(minion_config, default_flow_style=False)
)
out = self.run_call('-c {0} cmd.run "echo returnTOmaster"'.format(
os.path.join(integration.INTEGRATION_TEST_DIR, 'files', 'conf')))
jobs = [a for a in self.run_run('-c {0} jobs.list_jobs'.format(
os.path.join(integration.INTEGRATION_TEST_DIR, 'files', 'conf')))]
self.run_call('-c {0} cmd.run "echo returnTOmaster"'.format(self.get_config_dir()))
jobs = [a for a in self.run_run('-c {0} jobs.list_jobs'.format(self.get_config_dir()))]
self.assertTrue(True in ['returnTOmaster' in j for j in jobs])
# lookback jid
@ -129,38 +103,43 @@ class CallTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
assert idx > 0
assert jid
master_out = [
a for a in self.run_run('-c {0} jobs.lookup_jid {1}'.format(
os.path.join(integration.INTEGRATION_TEST_DIR,
'files',
'conf'),
jid))]
a for a in self.run_run('-c {0} jobs.lookup_jid {1}'.format(self.get_config_dir(), jid))
]
self.assertTrue(True in ['returnTOmaster' in a for a in master_out])
@skipIf(sys.platform.startswith('win'), 'This test does not apply on Win')
def test_issue_2731_masterless(self):
config_dir = '/tmp/salttest'
root_dir = os.path.join(integration.TMP, 'issue-2731')
config_dir = os.path.join(root_dir, 'conf')
minion_config_file = os.path.join(config_dir, 'minion')
logfile = os.path.join(root_dir, 'minion_test_issue_2731')
if not os.path.isdir(config_dir):
os.makedirs(config_dir)
master_config = yaml.load(open(self.get_config_file_path('master')).read())
master_root_dir = master_config['root_dir']
this_minion_key = os.path.join(
config_dir, 'pki', 'minions', 'minion_test_issue_2731'
master_root_dir, 'pki', 'minions', 'minion_test_issue_2731'
)
minion_config = {
'id': 'minion_test_issue_2731',
'master': 'localhost',
'master_port': 64506,
'root_dir': '/tmp/salttest',
'root_dir': master_root_dir,
'pki_dir': 'pki',
'cachedir': 'cachedir',
'sock_dir': 'minion_sock',
'open_mode': True,
'log_file': '/tmp/salttest/minion_test_issue_2731',
'log_file': logfile,
'log_level': 'quiet',
'log_level_logfile': 'info'
}
# Remove existing logfile
if os.path.isfile('/tmp/salttest/minion_test_issue_2731'):
os.unlink('/tmp/salttest/minion_test_issue_2731')
if os.path.isfile(logfile):
os.unlink(logfile)
start = datetime.now()
# Let's first test with a master running

View File

@ -109,6 +109,13 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
action='store_true',
help='Run unit tests'
)
self.test_selection_group.add_option(
'--fileserver-tests',
dest='fileserver',
default=False,
action='store_true',
help='Run Fileserver tests'
)
self.test_selection_group.add_option(
'-o',
'--outputter',
@ -137,7 +144,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
self.options.module, self.options.client, self.options.shell,
self.options.unit, self.options.state, self.options.runner,
self.options.loader, self.options.name, self.options.outputter,
os.geteuid() != 0, not self.options.run_destructive)):
self.options.fileserver, os.geteuid() != 0,
not self.options.run_destructive)):
self.error(
'No sense in generating the tests coverage report when '
'not running the full test suite, including the '
@ -149,7 +157,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
if not any((self.options.module, self.options.client,
self.options.shell, self.options.unit, self.options.state,
self.options.runner, self.options.loader,
self.options.name, self.options.outputter)):
self.options.name, self.options.outputter,
self.options.fileserver)):
self.options.module = True
self.options.client = True
self.options.shell = True
@ -158,6 +167,7 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
self.options.state = True
self.options.loader = True
self.options.outputter = True
self.options.fileserver = True
self.start_coverage(
branch=True,
@ -192,6 +202,7 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
self.options.client or
self.options.loader or
self.options.outputter or
self.options.fileserver or
named_tests):
# We're either not running any of runner, state, module and client
# tests, or, we're only running unittests by passing --unit or by
@ -240,7 +251,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
if not any([self.options.client, self.options.module,
self.options.runner, self.options.shell,
self.options.state, self.options.loader,
self.options.outputter, self.options.name]):
self.options.outputter, self.options.name,
self.options.fileserver]):
return status
with TestDaemon(self):
@ -264,6 +276,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
status.append(self.run_integration_suite('shell', 'Shell'))
if self.options.outputter:
status.append(self.run_integration_suite('output', 'Outputter'))
if self.options.fileserver:
status.append(self.run_integration_suite('fileserver', 'Fileserver'))
return status
def run_unit_tests(self):

View File

@ -14,7 +14,7 @@ ensure_in_syspath('../')
# Import Salt libs
import integration
from salt import client
from salt import client, config
from salt.exceptions import EauthAuthenticationError, SaltInvocationError
@ -22,15 +22,14 @@ from salt.exceptions import EauthAuthenticationError, SaltInvocationError
class LocalClientTestCase(TestCase,
integration.AdaptedConfigurationTestCaseMixIn):
def setUp(self):
if not os.path.exists('/tmp/salttest'):
# This path is hardcoded in the configuration file
os.makedirs('/tmp/salttest/cache')
master_config_path = self.get_config_file_path('master')
master_config = config.master_config(master_config_path)
if not os.path.exists(master_config['cachedir']):
os.makedirs(master_config['cachedir'])
if not os.path.exists(integration.TMP_CONF_DIR):
os.makedirs(integration.TMP_CONF_DIR)
self.local_client = client.LocalClient(
self.get_config_file_path('master')
)
self.local_client = client.LocalClient(mopts=master_config)
def test_create_local_client(self):
local_client = client.LocalClient(self.get_config_file_path('master'))

View File

@ -93,7 +93,7 @@ def _fopen_side_effect_etc_hosts(filename):
_unhandled_mock_read(filename)
class ConfigTestCase(TestCase):
class ConfigTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn):
def test_proper_path_joining(self):
fpath = tempfile.mktemp()
try:
@ -335,31 +335,28 @@ class ConfigTestCase(TestCase):
shutil.rmtree(tempdir)
def test_syndic_config(self):
syndic_conf_path = os.path.join(
integration.INTEGRATION_TEST_DIR, 'files', 'conf', 'syndic'
)
minion_config_path = os.path.join(
integration.INTEGRATION_TEST_DIR, 'files', 'conf', 'minion'
)
syndic_conf_path = self.get_config_file_path('syndic')
minion_conf_path = self.get_config_file_path('minion')
syndic_opts = sconfig.syndic_config(
syndic_conf_path, minion_config_path
syndic_conf_path, minion_conf_path
)
syndic_opts.update(salt.minion.resolve_dns(syndic_opts))
root_dir = syndic_opts['root_dir']
# id & pki dir are shared & so configured on the minion side
self.assertEqual(syndic_opts['id'], 'minion')
self.assertEqual(syndic_opts['pki_dir'], '/tmp/salttest/pki')
self.assertEqual(syndic_opts['pki_dir'], os.path.join(root_dir, 'pki'))
# the rest is configured master side
self.assertEqual(syndic_opts['master_uri'], 'tcp://127.0.0.1:54506')
self.assertEqual(syndic_opts['master_port'], 54506)
self.assertEqual(syndic_opts['master_ip'], '127.0.0.1')
self.assertEqual(syndic_opts['master'], 'localhost')
self.assertEqual(syndic_opts['sock_dir'], '/tmp/salttest/minion_sock')
self.assertEqual(syndic_opts['cachedir'], '/tmp/salttest/cachedir')
self.assertEqual(syndic_opts['log_file'], '/tmp/salttest/osyndic.log')
self.assertEqual(syndic_opts['pidfile'], '/tmp/salttest/osyndic.pid')
self.assertEqual(syndic_opts['sock_dir'], os.path.join(root_dir, 'minion_sock'))
self.assertEqual(syndic_opts['cachedir'], os.path.join(root_dir, 'cachedir'))
self.assertEqual(syndic_opts['log_file'], os.path.join(root_dir, 'osyndic.log'))
self.assertEqual(syndic_opts['pidfile'], os.path.join(root_dir, 'osyndic.pid'))
# Show that the options of localclient that repub to local master
# are not merged with syndic ones
self.assertEqual(syndic_opts['_master_conf_file'], minion_config_path)
self.assertEqual(syndic_opts['_master_conf_file'], minion_conf_path)
self.assertEqual(syndic_opts['_minion_conf_file'], syndic_conf_path)
def test_check_dns_deprecation_warning(self):