mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Merge remote-tracking branch 'saltstack/develop' into develop
This commit is contained in:
commit
84ce8160cf
7
debian/rules
vendored
7
debian/rules
vendored
@ -1,6 +1,13 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
ifeq ($(shell dpkg-vendor --is Ubuntu && echo yes),yes)
|
||||
UPSTART := pkg/*.upstart
|
||||
endif
|
||||
PKGFILES := pkg/*.service $(UPSTART)
|
||||
|
||||
%:
|
||||
dh $@
|
||||
cp $(PKGFILES) debian
|
||||
make -C doc html
|
||||
# overrides require debuilder >= 7.0.50
|
||||
# http://pkg-perl.alioth.debian.org/debhelper.html#forcing_special_tests
|
||||
|
10
debian/salt-master.service
vendored
10
debian/salt-master.service
vendored
@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=The Salt Master Server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/salt-master -d
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
debian/salt-minion.service
vendored
10
debian/salt-minion.service
vendored
@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=The Salt Minion
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/salt-minion -d
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
debian/salt-syndic.service
vendored
10
debian/salt-syndic.service
vendored
@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=The Salt Master Server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/salt-syndic -d
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -15,8 +15,9 @@ class Batch(object):
|
||||
'''
|
||||
Manage the execution of batch runs
|
||||
'''
|
||||
def __init__(self, opts):
|
||||
def __init__(self, opts, quiet=False):
|
||||
self.opts = opts
|
||||
self.quiet = quiet
|
||||
self.local = salt.client.LocalClient(opts['conf_file'])
|
||||
self.minions = self.__gather_minions()
|
||||
|
||||
@ -39,6 +40,7 @@ class Batch(object):
|
||||
fret = []
|
||||
for ret in self.local.cmd_iter(*args):
|
||||
for minion in ret:
|
||||
if not self.quiet:
|
||||
print('{0} Detected for this batch run'.format(minion))
|
||||
fret.append(minion)
|
||||
return sorted(fret)
|
||||
@ -58,6 +60,7 @@ class Batch(object):
|
||||
else:
|
||||
return int(self.opts['batch'])
|
||||
except ValueError:
|
||||
if not quiet:
|
||||
print(('Invalid batch data sent: {0}\nData must be in the form'
|
||||
'of %10, 10% or 3').format(self.opts['batch']))
|
||||
|
||||
@ -90,6 +93,7 @@ class Batch(object):
|
||||
active += next_
|
||||
args[0] = next_
|
||||
if next_:
|
||||
if not quiet:
|
||||
print('\nExecuting run on {0}\n'.format(next_))
|
||||
iters.append(
|
||||
self.local.cmd_iter_no_block(*args))
|
||||
@ -114,14 +118,15 @@ class Batch(object):
|
||||
pass
|
||||
for minion, data in parts.items():
|
||||
active.remove(minion)
|
||||
yield data['ret']
|
||||
ret[minion] = data['ret']
|
||||
data[minion] = data.pop('ret')
|
||||
if 'out' in data:
|
||||
out = data.pop('out')
|
||||
else:
|
||||
out = None
|
||||
if not self.quiet:
|
||||
salt.output.display_output(
|
||||
data,
|
||||
out,
|
||||
self.opts)
|
||||
return ret
|
||||
|
@ -216,6 +216,7 @@ def minion_config(path, check_dns=True):
|
||||
'update_url': False,
|
||||
'update_restart_services': [],
|
||||
'retry_dns': 30,
|
||||
'recon_max': 30000,
|
||||
}
|
||||
|
||||
|
||||
|
@ -630,6 +630,7 @@ class Minion(object):
|
||||
socket = context.socket(zmq.SUB)
|
||||
socket.setsockopt(zmq.SUBSCRIBE, '')
|
||||
socket.setsockopt(zmq.IDENTITY, self.opts['id'])
|
||||
socket.setsockopt(zmq.RECONNECT_IVL_MAX, self.opts['recon_max'])
|
||||
socket.connect(self.master_pub)
|
||||
poller.register(socket, zmq.POLLIN)
|
||||
epoller.register(epull_sock, zmq.POLLIN)
|
||||
|
@ -173,7 +173,11 @@ def cache_file(path, env='base'):
|
||||
salt '*' cp.cache_file salt://path/to/file
|
||||
'''
|
||||
client = salt.fileclient.get_file_client(__opts__)
|
||||
return client.cache_file(path, env)
|
||||
result = client.cache_file(path, env)
|
||||
if not result:
|
||||
log.error('Unable to cache file "{0}" from env '
|
||||
'"{1}".'.format(path,env))
|
||||
return result
|
||||
|
||||
|
||||
def cache_files(paths, env='base'):
|
||||
|
@ -617,6 +617,8 @@ def patch(originalfile, patchfile, options='', dry_run=False):
|
||||
dry_run_opt = ' -C'
|
||||
else:
|
||||
dry_run_opt = ' --dry-run'
|
||||
else:
|
||||
dry_run_opt = ''
|
||||
cmd = 'patch {0}{1} {2} {3}'.format(
|
||||
options, dry_run_opt, originalfile, patchfile)
|
||||
return __salt__['cmd.run_all'](cmd)
|
||||
|
84
salt/modules/keyboard.py
Normal file
84
salt/modules/keyboard.py
Normal file
@ -0,0 +1,84 @@
|
||||
'''
|
||||
Module for managing keyboards on posix-like systems.
|
||||
'''
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only work on posix-like systems
|
||||
'''
|
||||
# Disable on these platorms, specific service modules exist:
|
||||
disable = [
|
||||
'Windows',
|
||||
]
|
||||
if __grains__['os'] in disable:
|
||||
return False
|
||||
return 'keyboard'
|
||||
|
||||
|
||||
def get_sys():
|
||||
'''
|
||||
Get current system keyboard setting
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' keyboard.get_sys
|
||||
'''
|
||||
cmd = ''
|
||||
if 'Arch' in __grains__['os_family']:
|
||||
cmd = 'grep KEYMAP /etc/rc.conf | grep -vE "^#"'
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
cmd = 'grep LAYOUT /etc/sysconfig/keyboard | grep -vE "^#"'
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
cmd = 'grep XKBLAYOUT /etc/default/keyboard | grep -vE "^#"'
|
||||
out = __salt__['cmd.run'](cmd).split('=')
|
||||
ret = out[1].replace('"', '')
|
||||
return ret
|
||||
|
||||
|
||||
def set_sys(layout):
|
||||
'''
|
||||
Set current system keyboard setting
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' keyboard.set_sys dvorak
|
||||
'''
|
||||
if 'Arch' in __grains__['os_family']:
|
||||
__salt__['file.sed']('/etc/rc.conf', '^KEYMAP=.*', 'KEYMAP={0}'.format(layout))
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
__salt__['file.sed']('/etc/sysconfig/keyboard', '^LAYOUT=.*', 'LAYOUT={0}'.format(layout))
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
__salt__['file.sed']('/etc/default/keyboard', '^XKBLAYOUT=.*', 'XKBLAYOUT={0}'.format(layout))
|
||||
return layout
|
||||
|
||||
|
||||
def get_x():
|
||||
'''
|
||||
Get current X keyboard setting
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' keyboard.get_x
|
||||
'''
|
||||
cmd = 'setxkbmap -query | grep layout'
|
||||
out = __salt__['cmd.run'](cmd).split(':')
|
||||
return out[1].strip()
|
||||
|
||||
|
||||
def set_x(layout):
|
||||
'''
|
||||
Set current X keyboard setting
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' keyboard.set_x dvorak
|
||||
'''
|
||||
cmd = 'setxkbmap {0}'.format(layout)
|
||||
__salt__['cmd.run'](cmd)
|
||||
return layout
|
||||
|
@ -29,43 +29,42 @@ def _list_removed(old, new):
|
||||
return pkgs
|
||||
|
||||
|
||||
def _compare_versions(old, new):
|
||||
def _write_adminfile(kwargs):
|
||||
'''
|
||||
Returns a dict that that displays old and new versions for a package after
|
||||
install/upgrade of package.
|
||||
Create a temporary adminfile based on the keyword arguments passed to
|
||||
pkg.install.
|
||||
'''
|
||||
pkgs = {}
|
||||
for npkg in new:
|
||||
if npkg in old:
|
||||
if old[npkg] == new[npkg]:
|
||||
# no change in the package
|
||||
continue
|
||||
else:
|
||||
# the package was here before and the version has changed
|
||||
pkgs[npkg] = {'old': old[npkg],
|
||||
'new': new[npkg]}
|
||||
else:
|
||||
# the package is freshly installed
|
||||
pkgs[npkg] = {'old': '',
|
||||
'new': new[npkg]}
|
||||
return pkgs
|
||||
# Set the adminfile default variables
|
||||
email = kwargs.get('email', '')
|
||||
instance = kwargs.get('instance', 'quit')
|
||||
partial = kwargs.get('partial', 'nocheck')
|
||||
runlevel = kwargs.get('runlevel', 'nocheck')
|
||||
idepend = kwargs.get('idepend', 'nocheck')
|
||||
rdepend = kwargs.get('rdepend', 'nocheck')
|
||||
space = kwargs.get('space', 'nocheck')
|
||||
setuid = kwargs.get('setuid', 'nocheck')
|
||||
conflict = kwargs.get('conflict', 'nocheck')
|
||||
action = kwargs.get('action', 'nocheck')
|
||||
basedir = kwargs.get('basedir', 'default')
|
||||
|
||||
# Make tempfile to hold the adminfile contents.
|
||||
fd, adminfile = salt.utils.mkstemp(prefix="salt-", close_fd=False)
|
||||
|
||||
def _get_pkgs():
|
||||
'''
|
||||
Get a full list of the package installed on the machine
|
||||
'''
|
||||
pkg = {}
|
||||
cmd = '/usr/bin/pkginfo -x'
|
||||
# Write to file then close it.
|
||||
os.write(fd, 'email={0}\n'.format(email))
|
||||
os.write(fd, 'instance={0}\n'.format(instance))
|
||||
os.write(fd, 'partial={0}\n'.format(partial))
|
||||
os.write(fd, 'runlevel={0}\n'.format(runlevel))
|
||||
os.write(fd, 'idepend={0}\n'.format(idepend))
|
||||
os.write(fd, 'rdepend={0}\n'.format(rdepend))
|
||||
os.write(fd, 'space={0}\n'.format(space))
|
||||
os.write(fd, 'setuid={0}\n'.format(setuid))
|
||||
os.write(fd, 'conflict={0}\n'.format(conflict))
|
||||
os.write(fd, 'action={0}\n'.format(action))
|
||||
os.write(fd, 'basedir={0}\n'.format(basedir))
|
||||
os.close(fd)
|
||||
|
||||
line_count = 0
|
||||
for line in __salt__['cmd.run'](cmd).splitlines():
|
||||
if line_count % 2 == 0:
|
||||
namever = line.split()[0].strip()
|
||||
if line_count % 2 == 1:
|
||||
pkg[namever] = line.split()[1].strip()
|
||||
line_count = line_count + 1
|
||||
return pkg
|
||||
return adminfile
|
||||
|
||||
|
||||
def list_pkgs():
|
||||
@ -78,7 +77,17 @@ def list_pkgs():
|
||||
|
||||
salt '*' pkg.list_pkgs
|
||||
'''
|
||||
return _get_pkgs()
|
||||
pkg = {}
|
||||
cmd = '/usr/bin/pkginfo -x'
|
||||
|
||||
line_count = 0
|
||||
for line in __salt__['cmd.run'](cmd).splitlines():
|
||||
if line_count % 2 == 0:
|
||||
namever = line.split()[0].strip()
|
||||
if line_count % 2 == 1:
|
||||
pkg[namever] = line.split()[1].strip()
|
||||
line_count = line_count + 1
|
||||
return pkg
|
||||
|
||||
|
||||
def version(name):
|
||||
@ -109,7 +118,7 @@ def available_version(name):
|
||||
return version(name)
|
||||
|
||||
|
||||
def install(name, refresh=False, **kwargs):
|
||||
def install(name=None, refresh=False, sources=None, **kwargs):
|
||||
'''
|
||||
Install the passed package. Can install packages from the following
|
||||
sources::
|
||||
@ -127,18 +136,18 @@ def install(name, refresh=False, **kwargs):
|
||||
CLI Example, installing a datastream pkg that already exists on the
|
||||
minion::
|
||||
|
||||
salt '*' pkg.install <package name once installed> source=/dir/on/minion/<package filename>
|
||||
salt '*' pkg.install SMClgcc346 source=/var/spool/pkg/gcc-3.4.6-sol10-sparc-local.pkg
|
||||
salt '*' pkg.install sources='[{"<pkg name>": "/dir/on/minion/<pkg filename>"}]'
|
||||
salt '*' pkg.install sources='[{"SMClgcc346": "/var/spool/pkg/gcc-3.4.6-sol10-sparc-local.pkg"}]'
|
||||
|
||||
CLI Example, installing a datastream pkg that exists on the salt master::
|
||||
|
||||
salt '*' pkg.install <package name once installed> source='salt://srv/salt/pkgs/<package filename>'
|
||||
salt '*' pkg.install SMClgcc346 source='salt://srv/salt/pkgs/gcc-3.4.6-sol10-sparc-local.pkg'
|
||||
salt '*' pkg.install sources='[{"<pkg name>": "salt://pkgs/<pkg filename>"}]'
|
||||
salt '*' pkg.install sources='[{"SMClgcc346": "salt://pkgs/gcc-3.4.6-sol10-sparc-local.pkg"}]'
|
||||
|
||||
CLI Example, installing a datastream pkg that exists on a HTTP server::
|
||||
|
||||
salt '*' pkg.install <package name once installed> source='http://packages.server.com/<package filename>'
|
||||
salt '*' pkg.install SMClgcc346 source='http://packages.server.com/gcc-3.4.6-sol10-sparc-local.pkg'
|
||||
salt '*' pkg.install sources='[{"<pkg name>": "http://packages.server.com/<pkg filename>"}]'
|
||||
salt '*' pkg.install sources='[{"SMClgcc346": "http://packages.server.com/gcc-3.4.6-sol10-sparc-local.pkg"}]'
|
||||
|
||||
If working with solaris zones and you want to install a package only in the
|
||||
global zone you can pass 'current_zone_only=True' to salt to have the
|
||||
@ -149,7 +158,7 @@ def install(name, refresh=False, **kwargs):
|
||||
|
||||
CLI Example, installing a datastream package only in the global zone::
|
||||
|
||||
salt 'global_zone' pkg.install SMClgcc346 source=/var/spool/pkg/gcc-3.4.6-sol10-sparc-local.pkg current_zone_only=True
|
||||
salt 'global_zone' pkg.install sources='[{"SMClgcc346": "/var/spool/pkg/gcc-3.4.6-sol10-sparc-local.pkg"}]' current_zone_only=True
|
||||
|
||||
By default salt automatically provides an adminfile, to automate package
|
||||
installation, with these options set:
|
||||
@ -179,97 +188,72 @@ def install(name, refresh=False, **kwargs):
|
||||
CLI Example - Overriding the 'instance' adminfile option when calling the
|
||||
module directly::
|
||||
|
||||
salt '*' pkg.install <package name once installed> source='salt://srv/salt/pkgs/<package filename>' instance="overwrite"
|
||||
salt '*' pkg.install sources='[{"<pkg name>": "salt://pkgs/<pkg filename>"}]' instance="overwrite"
|
||||
|
||||
CLI Example - Overriding the 'instance' adminfile option when used in a
|
||||
state::
|
||||
|
||||
SMClgcc346:
|
||||
pkg.installed:
|
||||
- source: salt://srv/salt/pkgs/gcc-3.4.6-sol10-sparc-local.pkg
|
||||
- sources:
|
||||
- SMClgcc346: salt://srv/salt/pkgs/gcc-3.4.6-sol10-sparc-local.pkg
|
||||
- instance: overwrite
|
||||
|
||||
Note: the ID declaration is ignored, as the package name is read from the
|
||||
"sources" parameter.
|
||||
|
||||
CLI Example - Providing your own adminfile when calling the module
|
||||
directly::
|
||||
|
||||
salt '*' pkg.install <package name once installed> source='salt://srv/salt/pkgs/<package filename>' admin_source='salt://srv/salt/pkgs/<adminfile filename>'
|
||||
salt '*' pkg.install sources='[{"<pkg name>": "salt://pkgs/<pkg filename>"}]' admin_source='salt://pkgs/<adminfile filename>'
|
||||
|
||||
CLI Example - Providing your own adminfile when using states::
|
||||
|
||||
<package name once installed>:
|
||||
<pkg name>:
|
||||
pkg.installed:
|
||||
- source: salt://srv/salt/pkgs/<package filename>
|
||||
- admin_source: salt://srv/salt/pkgs/<adminfile filename>
|
||||
- sources:
|
||||
- <pkg name>: salt://pkgs/<pkg filename>
|
||||
- admin_source: salt://pkgs/<adminfile filename>
|
||||
|
||||
Note: the ID declaration is ignored, as the package name is read from the
|
||||
"sources" parameter.
|
||||
'''
|
||||
|
||||
if not 'source' in kwargs:
|
||||
return 'source option required with solaris pkg installs'
|
||||
else:
|
||||
if (kwargs['source']).startswith('salt://') \
|
||||
or (kwargs['source']).startswith('http://') \
|
||||
or (kwargs['source']).startswith('https://') \
|
||||
or (kwargs['source']).startswith('ftp://'):
|
||||
pkgname = __salt__['cp.cache_file'](kwargs['source'])
|
||||
else:
|
||||
pkgname = (kwargs['source'])
|
||||
pkg_params,pkg_type = __salt__['pkg_resource.parse_targets'](
|
||||
name,kwargs.get('pkgs'),sources)
|
||||
if pkg_params is None or len(pkg_params) == 0:
|
||||
return {}
|
||||
|
||||
if 'admin_source' in kwargs:
|
||||
adminfile = __salt__['cp.cache_file'](kwargs['admin_source'])
|
||||
else:
|
||||
# Set the adminfile default variables
|
||||
email = kwargs.get('email', '')
|
||||
instance = kwargs.get('instance', 'quit')
|
||||
partial = kwargs.get('partial', 'nocheck')
|
||||
runlevel = kwargs.get('runlevel', 'nocheck')
|
||||
idepend = kwargs.get('idepend', 'nocheck')
|
||||
rdepend = kwargs.get('rdepend', 'nocheck')
|
||||
space = kwargs.get('space', 'nocheck')
|
||||
setuid = kwargs.get('setuid', 'nocheck')
|
||||
conflict = kwargs.get('conflict', 'nocheck')
|
||||
action = kwargs.get('action', 'nocheck')
|
||||
basedir = kwargs.get('basedir', 'default')
|
||||
|
||||
# Make tempfile to hold the adminfile contents.
|
||||
fd, adminfile = salt.utils.mkstemp(prefix="salt-", close_fd=False)
|
||||
|
||||
# Write to file then close it.
|
||||
os.write(fd, 'email={0}\n'.format(email))
|
||||
os.write(fd, 'email={instance={0}\n'.format(instance))
|
||||
os.write(fd, 'email={partial={0}\n'.format(partial))
|
||||
os.write(fd, 'email={runlevel={0}\n'.format(runlevel))
|
||||
os.write(fd, 'email={idepend={0}\n'.format(idepend))
|
||||
os.write(fd, 'email={rdepend={0}\n'.format(rdepend))
|
||||
os.write(fd, 'email={space={0}\n'.format(space))
|
||||
os.write(fd, 'email={setuid={0}\n'.format(setuid))
|
||||
os.write(fd, 'email={conflict={0}\n'.format(conflict))
|
||||
os.write(fd, 'email={action={0}\n'.format(action))
|
||||
os.write(fd, 'email={basedir={0}\n'.format(basedir))
|
||||
os.close(fd)
|
||||
adminfile = _write_adminfile(kwargs)
|
||||
|
||||
# Get a list of the packages before install so we can diff after to see
|
||||
# what got installed.
|
||||
old = _get_pkgs()
|
||||
old = list_pkgs()
|
||||
|
||||
cmd = '/usr/sbin/pkgadd -n -a {0} '.format(adminfile)
|
||||
|
||||
# Global only?
|
||||
# Only makes sense in a global zone but works fine in non-globals.
|
||||
if kwargs.get('current_zone_only') == 'True':
|
||||
cmd += '-G '
|
||||
|
||||
cmd += '-d {0} \'all\''.format(pkgname)
|
||||
|
||||
# Install the package
|
||||
__salt__['cmd.retcode'](cmd)
|
||||
for pkg in pkg_params:
|
||||
temp_cmd = cmd + '-d {0} "all"'.format(pkg)
|
||||
# Install the package{s}
|
||||
stderr = __salt__['cmd.run_all'](temp_cmd).get('stderr','')
|
||||
if stderr:
|
||||
log.error(stderr)
|
||||
|
||||
# Get a list of the packages again, including newly installed ones.
|
||||
new = _get_pkgs()
|
||||
new = list_pkgs()
|
||||
|
||||
# Remove the temp adminfile
|
||||
if not 'admin_source' in kwargs:
|
||||
os.unlink(adminfile)
|
||||
|
||||
# Return a list of the new package installed.
|
||||
return _compare_versions(old, new)
|
||||
return __salt__['pkg_resource.find_changes'](old,new)
|
||||
|
||||
|
||||
def remove(name, **kwargs):
|
||||
@ -345,7 +329,7 @@ def remove(name, **kwargs):
|
||||
os.close(fd)
|
||||
|
||||
# Get a list of the currently installed pkgs.
|
||||
old = _get_pkgs()
|
||||
old = list_pkgs()
|
||||
|
||||
# Remove the package
|
||||
cmd = '/usr/sbin/pkgrm -n -a {0} {1}'.format(adminfile, name)
|
||||
@ -356,7 +340,7 @@ def remove(name, **kwargs):
|
||||
os.unlink(adminfile)
|
||||
|
||||
# Get a list of the packages after the uninstall
|
||||
new = _get_pkgs()
|
||||
new = list_pkgs()
|
||||
|
||||
# Compare the pre and post remove package objects and report the
|
||||
# uninstalled pkgs.
|
||||
|
@ -14,6 +14,11 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Set the virtual pkg module if the os is Windows
|
||||
@ -40,7 +45,7 @@ def available_version(name):
|
||||
|
||||
salt '*' pkg.available_version <package name>
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
return 'pkg.available_version not implemented on Windows yet'
|
||||
|
||||
|
||||
def upgrade_available(name):
|
||||
@ -51,7 +56,8 @@ def upgrade_available(name):
|
||||
|
||||
salt '*' pkg.upgrade_available <package name>
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.upgrade_available not implemented on Windows yet')
|
||||
return False
|
||||
|
||||
|
||||
def list_upgrades():
|
||||
@ -62,7 +68,8 @@ def list_upgrades():
|
||||
|
||||
salt '*' pkg.list_upgrades
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.list_upgrades not implemented on Windows yet')
|
||||
return {}
|
||||
|
||||
|
||||
def version(name):
|
||||
@ -263,10 +270,11 @@ def refresh_db():
|
||||
|
||||
salt '*' pkg.refresh_db
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.refresh_db not implemented on Windows yet')
|
||||
return {}
|
||||
|
||||
|
||||
def install(name, refresh=False, **kwargs):
|
||||
def install(name=None, refresh=False, **kwargs):
|
||||
'''
|
||||
Install the passed package
|
||||
|
||||
@ -279,7 +287,8 @@ def install(name, refresh=False, **kwargs):
|
||||
|
||||
salt '*' pkg.install <package name>
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.install not implemented on Windows yet')
|
||||
return {}
|
||||
|
||||
|
||||
def upgrade():
|
||||
@ -295,7 +304,8 @@ def upgrade():
|
||||
|
||||
salt '*' pkg.upgrade
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.upgrade not implemented on Windows yet')
|
||||
return {}
|
||||
|
||||
|
||||
def remove(name):
|
||||
@ -308,7 +318,8 @@ def remove(name):
|
||||
|
||||
salt '*' pkg.remove <package name>
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.remove not implemented on Windows yet')
|
||||
return []
|
||||
|
||||
|
||||
def purge(name):
|
||||
@ -322,4 +333,5 @@ def purge(name):
|
||||
|
||||
salt '*' pkg.purge <package name>
|
||||
'''
|
||||
return 'Not implemented on Windows yet'
|
||||
log.warning('pkg.purge not implemented on Windows yet')
|
||||
return []
|
||||
|
@ -175,7 +175,18 @@ def version(name):
|
||||
|
||||
salt '*' pkg.version <package name>
|
||||
'''
|
||||
# since list_pkgs is used to support matching complex versions
|
||||
# we can search for a digit in the name and if one doesn't exist
|
||||
# then just use the dbMatch function, which is 1000 times quicker
|
||||
m = re.search("[0-9]", name)
|
||||
if m:
|
||||
pkgs = list_pkgs(name)
|
||||
else:
|
||||
ts = rpm.TransactionSet()
|
||||
mi = ts.dbMatch('name', name)
|
||||
pkgs = {}
|
||||
for h in mi:
|
||||
pkgs[h['name']] = "-".join([h['version'], h['release']])
|
||||
if name in pkgs:
|
||||
return pkgs[name]
|
||||
else:
|
||||
|
4
setup.py
4
setup.py
@ -230,6 +230,10 @@ else:
|
||||
'scripts/salt-call',
|
||||
'scripts/salt-run',
|
||||
'scripts/salt']
|
||||
# Distutils does not know what these are and throws warnings.
|
||||
# Stop the warning.
|
||||
setup_kwargs.pop('install_requires')
|
||||
setup_kwargs.pop('zip_safe')
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(**setup_kwargs)
|
||||
|
@ -56,7 +56,6 @@ SYS_TMP_DIR = tempfile.gettempdir()
|
||||
TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir')
|
||||
FILES = os.path.join(INTEGRATION_TEST_DIR, 'files')
|
||||
MOCKBIN = os.path.join(INTEGRATION_TEST_DIR, 'mockbin')
|
||||
MINIONS_CONNECT_TIMEOUT = MINIONS_SYNC_TIMEOUT = 60
|
||||
|
||||
|
||||
def print_header(header, sep='~', top=True, bottom=True, inline=False,
|
||||
@ -128,6 +127,7 @@ class TestDaemon(object):
|
||||
'''
|
||||
Set up the master and minion daemons, and run related cases
|
||||
'''
|
||||
MINIONS_CONNECT_TIMEOUT = MINIONS_SYNC_TIMEOUT = 60
|
||||
|
||||
def __init__(self, opts=None):
|
||||
self.opts = opts
|
||||
@ -250,33 +250,9 @@ class TestDaemon(object):
|
||||
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
|
||||
)
|
||||
|
||||
evt_minions_connect = multiprocessing.Event()
|
||||
evt_minions_sync = multiprocessing.Event()
|
||||
minion_targets = set(['minion', 'sub_minion'])
|
||||
|
||||
# Wait for minions to connect back
|
||||
wait_minions_connection = multiprocessing.Process(
|
||||
target=self.__wait_for_minions_connections,
|
||||
args=(evt_minions_connect, minion_targets)
|
||||
)
|
||||
wait_minions_connection.start()
|
||||
if evt_minions_connect.wait(MINIONS_CONNECT_TIMEOUT) is False:
|
||||
print('WARNING: Minions failed to connect back. Tests requiring '
|
||||
'them WILL fail')
|
||||
wait_minions_connection.terminate()
|
||||
del(evt_minions_connect, wait_minions_connection)
|
||||
|
||||
# Wait for minions to "sync_all"
|
||||
sync_minions = multiprocessing.Process(
|
||||
target=self.__sync_minions,
|
||||
args=(evt_minions_sync, minion_targets)
|
||||
)
|
||||
sync_minions.start()
|
||||
if evt_minions_sync.wait(MINIONS_SYNC_TIMEOUT) is False:
|
||||
print('WARNING: Minions failed to sync. Tests requiring the '
|
||||
'testing `runtests_helper` module WILL fail')
|
||||
sync_minions.terminate()
|
||||
del(evt_minions_sync, sync_minions)
|
||||
self.minion_targets = set(['minion', 'sub_minion'])
|
||||
self.pre_setup_minions()
|
||||
self.setup_minions()
|
||||
|
||||
if self.opts.sysinfo:
|
||||
from salt import version
|
||||
@ -292,7 +268,10 @@ class TestDaemon(object):
|
||||
|
||||
print_header('', sep='=', inline=True)
|
||||
|
||||
try:
|
||||
return self
|
||||
finally:
|
||||
self.post_setup_minions()
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
'''
|
||||
@ -306,6 +285,43 @@ class TestDaemon(object):
|
||||
self._exit_mockbin()
|
||||
self._clean()
|
||||
|
||||
def pre_setup_minions(self):
|
||||
"""
|
||||
Subclass this method for additional minion setups.
|
||||
"""
|
||||
|
||||
def setup_minions(self):
|
||||
# Wait for minions to connect back
|
||||
wait_minion_connections = multiprocessing.Process(
|
||||
target=self.wait_for_minion_connections,
|
||||
args=(self.minion_targets, self.MINIONS_CONNECT_TIMEOUT)
|
||||
)
|
||||
wait_minion_connections.start()
|
||||
wait_minion_connections.join()
|
||||
wait_minion_connections.terminate()
|
||||
if wait_minion_connections.exitcode > 0:
|
||||
return False
|
||||
|
||||
del(wait_minion_connections)
|
||||
|
||||
# Wait for minions to "sync_all"
|
||||
sync_minions = multiprocessing.Process(
|
||||
target=self.sync_minions,
|
||||
args=(self.minion_targets, self.MINIONS_SYNC_TIMEOUT)
|
||||
)
|
||||
sync_minions.start()
|
||||
sync_minions.join()
|
||||
if sync_minions.exitcode > 0:
|
||||
return False
|
||||
sync_minions.terminate()
|
||||
del(sync_minions)
|
||||
return True
|
||||
|
||||
def post_setup_minions(self):
|
||||
"""
|
||||
Subclass this method to execute code after the minions have been setup
|
||||
"""
|
||||
|
||||
def _enter_mockbin(self):
|
||||
path = os.environ.get('PATH', '')
|
||||
path_items = path.split(os.pathsep)
|
||||
@ -338,14 +354,22 @@ class TestDaemon(object):
|
||||
shutil.rmtree(TMP)
|
||||
|
||||
def wait_for_jid(self, targets, jid, timeout=120):
|
||||
time.sleep(1) # Allow some time for minions to accept jobs
|
||||
now = datetime.now()
|
||||
expire = now + timedelta(seconds=timeout)
|
||||
job_finished = False
|
||||
while now <= expire:
|
||||
running = self.__client_job_running(targets, jid)
|
||||
sys.stdout.write('\r' + ' ' * PNUM + '\r')
|
||||
if not running:
|
||||
print
|
||||
if not running and job_finished is False:
|
||||
# Let's not have false positives and wait one more seconds
|
||||
job_finished = True
|
||||
elif not running and job_finished is True:
|
||||
return True
|
||||
elif running and job_finished is True:
|
||||
job_finished = False
|
||||
|
||||
if job_finished is False:
|
||||
sys.stdout.write(
|
||||
' * {YELLOW}[Quit in {0}]{ENDC} Waiting for {1}'.format(
|
||||
'{0}'.format(expire - now).rsplit('.', 1)[0],
|
||||
@ -354,11 +378,13 @@ class TestDaemon(object):
|
||||
)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
timeout -= 1
|
||||
time.sleep(1)
|
||||
now = datetime.now()
|
||||
else:
|
||||
sys.stdout.write('\n * ERROR: Failed to get information back\n')
|
||||
sys.stdout.write(
|
||||
'\n {RED_BOLD}*{ENDC} ERROR: Failed to get information '
|
||||
'back\n'.format(**self.colors)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
return False
|
||||
|
||||
@ -370,35 +396,71 @@ class TestDaemon(object):
|
||||
k for (k, v) in running.iteritems() if v and v[0]['jid'] == jid
|
||||
]
|
||||
|
||||
def __wait_for_minions_connections(self, evt, targets):
|
||||
print_header(
|
||||
'Waiting at most {0} secs for local minions to connect '
|
||||
'back and another {1} secs for them to '
|
||||
'"saltutil.sync_all()"'.format(
|
||||
MINIONS_CONNECT_TIMEOUT, MINIONS_SYNC_TIMEOUT
|
||||
), sep='=', centered=True
|
||||
def wait_for_minion_connections(self, targets, timeout):
|
||||
sys.stdout.write(
|
||||
' {LIGHT_BLUE}*{ENDC} Waiting at most {0} for minions({1}) to '
|
||||
'connect back\n'.format(
|
||||
(timeout > 60 and
|
||||
timedelta(seconds=timeout) or
|
||||
'{0} secs'.format(timeout)),
|
||||
', '.join(targets),
|
||||
**self.colors
|
||||
)
|
||||
targets = set(['minion', 'sub_minion'])
|
||||
)
|
||||
sys.stdout.flush()
|
||||
expected_connections = set(targets)
|
||||
while True:
|
||||
# If enough time passes, a timeout will be triggered by
|
||||
# multiprocessing.Event, so, we can have this while True here
|
||||
targets = self.client.cmd('*', 'test.ping')
|
||||
for target in targets:
|
||||
now = datetime.now()
|
||||
expire = now + timedelta(seconds=timeout)
|
||||
while now <= expire:
|
||||
sys.stdout.write('\r' + ' ' * PNUM + '\r')
|
||||
sys.stdout.write(
|
||||
' * {YELLOW}[Quit in {0}]{ENDC} Waiting for {1}'.format(
|
||||
'{0}'.format(expire - now).rsplit('.', 1)[0],
|
||||
', '.join(expected_connections),
|
||||
**self.colors
|
||||
)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
|
||||
responses = self.client.cmd(
|
||||
','.join(expected_connections), 'test.ping', expr_form='list',
|
||||
)
|
||||
for target in responses:
|
||||
if target not in expected_connections:
|
||||
# Someone(minion) else "listening"?
|
||||
print target
|
||||
continue
|
||||
expected_connections.remove(target)
|
||||
print(' * {0} minion connected'.format(target))
|
||||
if not expected_connections:
|
||||
# All expected connections have connected
|
||||
break
|
||||
time.sleep(1)
|
||||
evt.set()
|
||||
sys.stdout.write('\r' + ' ' * PNUM + '\r')
|
||||
sys.stdout.write(
|
||||
' {LIGHT_GREEN}*{ENDC} {0} connected.\n'.format(
|
||||
target, **self.colors
|
||||
)
|
||||
)
|
||||
sys.stdout.flush()
|
||||
|
||||
def __sync_minions(self, evt, targets):
|
||||
if not expected_connections:
|
||||
return
|
||||
|
||||
time.sleep(1)
|
||||
now = datetime.now()
|
||||
else:
|
||||
print(
|
||||
'\n {RED_BOLD}*{ENDC} WARNING: Minions failed to connect '
|
||||
'back. Tests requiring them WILL fail'.format(**self.colors)
|
||||
)
|
||||
print_header('=', sep='=', inline=True)
|
||||
raise SystemExit()
|
||||
|
||||
def sync_minions(self, targets, timeout=120):
|
||||
# Let's sync all connected minions
|
||||
print(' * Syncing local minion\'s dynamic data(saltutil.sync_all)')
|
||||
print(
|
||||
' {LIGHT_BLUE}*{ENDC} Syncing minion\'s dynamic '
|
||||
'data(saltutil.sync_all)'.format(
|
||||
', '.join(targets),
|
||||
**self.colors
|
||||
)
|
||||
)
|
||||
syncing = set(targets)
|
||||
jid_info = self.client.run_job(
|
||||
','.join(targets), 'saltutil.sync_all',
|
||||
@ -406,23 +468,33 @@ class TestDaemon(object):
|
||||
timeout=9999999999999999,
|
||||
)
|
||||
|
||||
if self.wait_for_jid(targets, jid_info['jid']) is False:
|
||||
evt.set()
|
||||
return
|
||||
if self.wait_for_jid(targets, jid_info['jid'], timeout) is False:
|
||||
print(
|
||||
' {RED_BOLD}*{ENDC} WARNING: Minions failed to sync. Tests '
|
||||
'requiring custom modules WILL fail'.format(**self.colors)
|
||||
)
|
||||
raise SystemExit()
|
||||
|
||||
while syncing:
|
||||
rdata = self.client.get_returns(jid_info['jid'], syncing, 1)
|
||||
if rdata:
|
||||
for idx, (name, output) in enumerate(rdata.iteritems()):
|
||||
print(' * Synced {0}: {1}'.format(name, output))
|
||||
for name, output in rdata.iteritems():
|
||||
print(
|
||||
' {LIGHT_GREEN}*{ENDC} Synced {0}: modules=>{1} '
|
||||
'states=>{2} grains=>{3} renderers=>{4} '
|
||||
'returners=>{5}'.format(
|
||||
name, *output, **self.colors
|
||||
)
|
||||
)
|
||||
# Synced!
|
||||
try:
|
||||
syncing.remove(name)
|
||||
except KeyError:
|
||||
print(' * {0} already synced??? {1}'.format(
|
||||
name, output
|
||||
))
|
||||
evt.set()
|
||||
print(
|
||||
' {RED_BOLD}*{ENDC} {0} already synced??? '
|
||||
'{1}'.format(name, output, **self.colors)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
class ModuleCase(TestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user