mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
Merge remote-tracking branch 'upstream/develop' into sam_raet_10
This commit is contained in:
commit
f466161fa0
@ -184,7 +184,7 @@ dummy-variables-rgx=_|dummy
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=__opts__,__salt__,__pillar__,__grains__,__context__,__ret__,__env__,__low__,__lowstate__,__running__,__active_provider_name__
|
||||
additional-builtins=__opts__,__salt__,__pillar__,__grains__,__context__,__ret__,__env__,__low__,__states__,__lowstate__,__running__,__active_provider_name__
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
@ -142,7 +142,7 @@ indent-string=' '
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=__opts__,__salt__,__pillar__,__grains__,__context__,__ret__,__env__,__low__,__lowstate__,__running__,__active_provider_name__
|
||||
additional-builtins=__opts__,__salt__,__pillar__,__grains__,__context__,__ret__,__env__,__low__,__states__,__lowstate__,__running__,__active_provider_name__
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
@ -67,7 +67,7 @@ salt's code.
|
||||
|
||||
.. _`report an issue`: https://github.com/saltstack/salt/issues
|
||||
.. _`Salt's documentation`: http://docs.saltstack.org/en/latest/index.html
|
||||
.. _`Developing Salt`: http://docs.saltstack.org/en/latest/topics/community.html#developing-salt
|
||||
.. _`pull request`: http://docs.saltstack.org/en/latest/topics/community.html#sending-a-github-pull-request
|
||||
.. _`Developing Salt`: http://docs.saltstack.com/topics/hacking.html
|
||||
.. _`pull request`: http://docs.saltstack.com/topics/hacking.html#sending-a-github-pull-request
|
||||
|
||||
.. vim: fenc=utf-8 spell spl=en
|
||||
|
@ -117,7 +117,7 @@ copyright = '2014 SaltStack, Inc.'
|
||||
|
||||
version = salt.version.__version__
|
||||
#release = '.'.join(map(str, salt.version.__version_info__))
|
||||
release = '2014.1'
|
||||
release = '2014.1.0'
|
||||
|
||||
language = 'en'
|
||||
locale_dirs = [
|
||||
|
@ -223,4 +223,5 @@ Full list of builtin execution modules
|
||||
zcbuildout
|
||||
zfs
|
||||
zpool
|
||||
znc
|
||||
zypper
|
||||
|
@ -4,3 +4,4 @@ salt.modules.img
|
||||
|
||||
.. automodule:: salt.modules.img
|
||||
:members:
|
||||
:exclude-members: mnt_image
|
||||
|
6
doc/ref/modules/all/salt.modules.znc.rst
Normal file
6
doc/ref/modules/all/salt.modules.znc.rst
Normal file
@ -0,0 +1,6 @@
|
||||
================
|
||||
salt.modules.znc
|
||||
================
|
||||
|
||||
.. automodule:: salt.modules.znc
|
||||
:members:
|
@ -15,6 +15,7 @@ Full list of builtin renderer modules
|
||||
mako
|
||||
py
|
||||
pydsl
|
||||
pyobjects
|
||||
stateconf
|
||||
wempy
|
||||
yaml
|
||||
|
6
doc/ref/renderers/all/salt.renderers.pyobjects.rst
Normal file
6
doc/ref/renderers/all/salt.renderers.pyobjects.rst
Normal file
@ -0,0 +1,6 @@
|
||||
========================
|
||||
salt.renderers.pyobjects
|
||||
========================
|
||||
|
||||
.. automodule:: salt.renderers.pyobjects
|
||||
:members:
|
@ -14,7 +14,7 @@ documents. But since the only thing the state system cares about is raw data,
|
||||
the SLS files can be any structured format that can be dreamed up.
|
||||
|
||||
Currently there is support for ``Jinja + YAML``, ``Mako + YAML``,
|
||||
``Wempy + YAML``, ``Jinja + json`` ``Mako + json`` and ``Wempy + json``.
|
||||
``Wempy + YAML``, ``Jinja + json``, ``Mako + json`` and ``Wempy + json``.
|
||||
|
||||
Renderers can be written to support any template type. This means that the
|
||||
Salt states could be managed by XML files, HTML files, Puppet files, or any
|
||||
@ -148,4 +148,3 @@ Here is a simple YAML renderer example:
|
||||
yaml_data = yaml_data.read()
|
||||
data = yaml.load(yaml_data)
|
||||
return data if data else {}
|
||||
|
||||
|
@ -11,21 +11,24 @@ Installation
|
||||
Quick Install
|
||||
-------------
|
||||
|
||||
Many popular distributions will be able to install the salt minion by executing
|
||||
the bootstrap script:
|
||||
On most distributions, you can set up a **Salt Minion** with the bootstrap script:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget -O - http://bootstrap.saltstack.org | sudo sh
|
||||
curl -L http://bootstrap.saltstack.org | sudo sh
|
||||
|
||||
Run the following script to install just the Salt Master:
|
||||
or, to connect immediately to a running Salt Master:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -L http://bootstrap.saltstack.org | sudo sh -s -- -A saltmaster.example.com
|
||||
|
||||
To set up a **Salt Master**:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -L http://bootstrap.saltstack.org | sudo sh -s -- -M -N
|
||||
|
||||
The script should also make it simple to install a salt master, if desired.
|
||||
|
||||
Currently the install script has been tested to work on:
|
||||
|
||||
* Ubuntu 10.x/11.x/12.x
|
||||
|
@ -106,7 +106,9 @@ same way as in the above example, only without a top-level ``grains:`` key:
|
||||
Matching Grains in the Top File
|
||||
===============================
|
||||
|
||||
With correctly setup grains on the Minion, the Top file used in Pillar or during Highstate can be made really efficient. Like for example, you could do:
|
||||
With correctly configured grains on the Minion, the :term:`top file` used in
|
||||
Pillar or during Highstate can be made very efficient. For example, consider
|
||||
the following configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -126,17 +128,28 @@ With correctly setup grains on the Minion, the Top file used in Pillar or during
|
||||
- match: grain
|
||||
- lb
|
||||
|
||||
For this example to work, you would need the grain ``node_type`` and the correct value to match on. This simple example is nice, but too much of the code is similar. To go one step further, we can place some Jinja template code into the Top file.
|
||||
For this example to work, you would need to have defined the grain
|
||||
``node_type`` for the minions you wish to match. This simple example is nice,
|
||||
but too much of the code is similar. To go one step further, Jinja templating
|
||||
can be used to simplify the the :term:`top file`.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
{% set self = grains['node_type'] %}
|
||||
{% set node_type = salt['grains.get']('node_type', '') %}
|
||||
|
||||
{% if node_type %}
|
||||
'node_type:{{ self }}':
|
||||
- match: grain
|
||||
- {{ self }}
|
||||
{% endif %}
|
||||
|
||||
The Jinja code simplified the Top file, and allowed SaltStack to work its magic.
|
||||
Using Jinja templating, only one match entry needs to be defined.
|
||||
|
||||
.. note::
|
||||
|
||||
The example above uses the :mod:`grains.get <salt.modules.grains.get>`
|
||||
function to account for minions which do not have the ``node_type`` grain
|
||||
set.
|
||||
|
||||
.. _writing-grains:
|
||||
|
||||
|
@ -23,9 +23,9 @@ nodegroups. Here's an example nodegroup configuration within
|
||||
|
||||
.. note::
|
||||
|
||||
The 'L' within group1 is matching a list of minions, while the 'G' in
|
||||
group2 is matching specific grains. See the
|
||||
:doc:`compound matchers <compound>` documentation for more details.
|
||||
The ``L`` within group1 is matching a list of minions, while the ``G`` in
|
||||
group2 is matching specific grains. See the :doc:`compound matchers
|
||||
<compound>` documentation for more details.
|
||||
|
||||
To match a nodegroup on the CLI, use the ``-N`` command-line option:
|
||||
|
||||
@ -33,8 +33,8 @@ To match a nodegroup on the CLI, use the ``-N`` command-line option:
|
||||
|
||||
salt -N group1 test.ping
|
||||
|
||||
To match in your :term:`top file`, make sure to put ``- match: nodegroup`` on
|
||||
the line directly following the nodegroup name.
|
||||
To match a nodegroup in your :term:`top file`, make sure to put ``- match:
|
||||
nodegroup`` on the line directly following the nodegroup name.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -27,6 +27,18 @@ available to Salt.
|
||||
Simple Configuration
|
||||
====================
|
||||
|
||||
.. note::
|
||||
|
||||
GitFS requires the Python module ``GitPython``, version 0.3.0 or newer.
|
||||
If your Master runs Ubuntu 12.04 LTS, you will likely need to install
|
||||
GitPython using `pip`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# pip install GitPython
|
||||
|
||||
.. _`pip`: http://www.pip-installer.org/
|
||||
|
||||
To use the gitfs backend only two configuration changes are required on the
|
||||
master. The ``fileserver_backend`` option needs to be set with a value of
|
||||
``git``:
|
||||
@ -205,19 +217,6 @@ In order to configure a ``gitfs_remotes`` repository over SSH transport the
|
||||
The private key used to connect to the repository must be located in ``~/.ssh/id_rsa``
|
||||
for the user running the salt-master.
|
||||
|
||||
.. note::
|
||||
|
||||
GitFS requires the Python module ``GitPython``, version 0.3.0 or newer.
|
||||
If your Master runs Ubuntu 12.04 LTS, you will likely need to install
|
||||
GitPython using `pip`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# pip install GitPython
|
||||
|
||||
.. _`pip`: http://www.pip-installer.org/
|
||||
|
||||
|
||||
Using Git as an External Pillar Source
|
||||
======================================
|
||||
|
||||
|
@ -31,7 +31,9 @@ master config file.
|
||||
3. Distribute the minion keys.
|
||||
|
||||
There is no single method to get the keypair to your minion. The difficulty is
|
||||
finding a distribution method which is secure.
|
||||
finding a distribution method which is secure. For Amazon EC2 only, an AWS best
|
||||
practice is to use IAM Roles to pass credentials. (See blog post,
|
||||
http://blogs.aws.amazon.com/php/post/Tx1F82CR0ANO3ZI/Providing-credentials-to-the-AWS-SDK-for-PHP )
|
||||
|
||||
.. admonition:: Security Warning
|
||||
|
||||
|
@ -204,7 +204,7 @@ class SSH(object):
|
||||
)
|
||||
pub = '{0}.pub'.format(priv)
|
||||
with open(pub, 'r') as fp_:
|
||||
return '{0} root@master'.format(fp_.read().split()[1])
|
||||
return '{0} rsa root@master'.format(fp_.read().split()[1])
|
||||
|
||||
def key_deploy(self, host, ret):
|
||||
'''
|
||||
@ -255,7 +255,7 @@ class SSH(object):
|
||||
self.opts['arg_str'],
|
||||
host,
|
||||
**target)
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, retcode = single.cmd_block()
|
||||
try:
|
||||
data = salt.utils.find_json(stdout)
|
||||
return {host: data.get('local', data)}
|
||||
@ -276,25 +276,27 @@ class SSH(object):
|
||||
host,
|
||||
**target)
|
||||
ret = {'id': single.id}
|
||||
stdout, stderr = single.run()
|
||||
stdout, stderr, retcode = single.run()
|
||||
if stdout.startswith('deploy'):
|
||||
single.deploy()
|
||||
stdout, stderr = single.run()
|
||||
stdout, stderr, retcode = single.run()
|
||||
# This job is done, yield
|
||||
try:
|
||||
if not stdout and stderr:
|
||||
if 'Permission denied' in stderr:
|
||||
ret['ret'] = 'Permission denied'
|
||||
else:
|
||||
ret['ret'] = stderr
|
||||
else:
|
||||
data = salt.utils.find_json(stdout)
|
||||
if len(data) < 2 and 'local' in data:
|
||||
ret['ret'] = data['local']
|
||||
else:
|
||||
ret['ret'] = data
|
||||
ret['ret'] = {
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'retcode': retcode,
|
||||
}
|
||||
except Exception:
|
||||
ret['ret'] = stdout
|
||||
ret['ret'] = {
|
||||
'stdout': stdout,
|
||||
'stderr': stderr,
|
||||
'retcode': retcode,
|
||||
}
|
||||
que.put(ret)
|
||||
|
||||
def handle_ssh(self):
|
||||
@ -503,27 +505,27 @@ class Single(object):
|
||||
If a (re)deploy is needed, then retry the operation after a deploy
|
||||
attempt
|
||||
|
||||
Returns tuple of (stdout, stderr)
|
||||
Returns tuple of (stdout, stderr, retcode)
|
||||
'''
|
||||
stdout, stderr = None, None
|
||||
stdout = stderr = retcode = None
|
||||
arg_str = self.arg_str
|
||||
|
||||
if self.opts.get('raw_shell'):
|
||||
if not arg_str.startswith(('"', "'")) and not arg_str.endswith(('"', "'")):
|
||||
arg_str = "'{0}'".format(arg_str)
|
||||
stdout, stderr = self.shell.exec_cmd(arg_str)
|
||||
stdout, stderr, retcode = self.shell.exec_cmd(arg_str)
|
||||
|
||||
elif self.fun in self.wfuncs:
|
||||
stdout, stderr = self.run_wfunc()
|
||||
stdout, stderr, retcode = self.run_wfunc()
|
||||
|
||||
else:
|
||||
stdout, stderr = self.cmd_block()
|
||||
stdout, stderr, retcode = self.cmd_block()
|
||||
|
||||
if stdout.startswith('deploy') and not deploy_attempted:
|
||||
self.deploy()
|
||||
return self.run(deploy_attempted=True)
|
||||
|
||||
return stdout, stderr
|
||||
return stdout, stderr, retcode
|
||||
|
||||
def run_wfunc(self):
|
||||
'''
|
||||
@ -587,7 +589,7 @@ class Single(object):
|
||||
self.wfuncs = salt.loader.ssh_wrapper(opts, wrapper)
|
||||
wrapper.wfuncs = self.wfuncs
|
||||
ret = json.dumps(self.wfuncs[self.fun](*self.arg))
|
||||
return ret, ''
|
||||
return ret, '', None
|
||||
|
||||
def cmd(self):
|
||||
'''
|
||||
@ -613,8 +615,8 @@ class Single(object):
|
||||
self.opts['hash_type'],
|
||||
thin_sum,
|
||||
self.minion_config)
|
||||
for stdout, stderr in self.shell.exec_nb_cmd(cmd):
|
||||
yield stdout, stderr
|
||||
for stdout, stderr, retcode in self.shell.exec_nb_cmd(cmd):
|
||||
yield stdout, stderr, retcode
|
||||
|
||||
def cmd_block(self, is_retry=False):
|
||||
'''
|
||||
@ -641,26 +643,32 @@ class Single(object):
|
||||
thin_sum,
|
||||
self.minion_config)
|
||||
log.debug('Performing shimmed command as follows:\n{0}'.format(cmd))
|
||||
stdout, stderr = self.shell.exec_cmd(cmd)
|
||||
stdout, stderr, retcode = self.shell.exec_cmd(cmd)
|
||||
|
||||
log.debug('STDOUT {1}\n{0}'.format(stdout, self.target['host']))
|
||||
log.debug('STDERR {1}\n{0}'.format(stderr, self.target['host']))
|
||||
log.debug('RETCODE {1}\n{0}'.format(retcode, self.target['host']))
|
||||
|
||||
error = self.categorize_shim_errors(stdout, stderr)
|
||||
error = self.categorize_shim_errors(stdout, stderr, retcode)
|
||||
if error:
|
||||
return 'ERROR: {0}'.format(error), stderr
|
||||
return 'ERROR: {0}'.format(error), stderr, retcode
|
||||
|
||||
if RSTR in stdout:
|
||||
stdout = stdout.split(RSTR)[1].strip()
|
||||
if stdout.startswith('deploy'):
|
||||
self.deploy()
|
||||
stdout, stderr = self.shell.exec_cmd(cmd)
|
||||
stdout, stderr, retcode = self.shell.exec_cmd(cmd)
|
||||
if RSTR in stdout:
|
||||
stdout = stdout.split(RSTR)[1].strip()
|
||||
|
||||
return stdout, stderr
|
||||
return stdout, stderr, retcode
|
||||
|
||||
def categorize_shim_errors(self, stdout, stderr, retcode):
|
||||
# Unused stdout and retcode for now but these may be used to
|
||||
# categorize errors
|
||||
_ = stdout
|
||||
_ = retcode
|
||||
|
||||
def categorize_shim_errors(self, stdout, stderr):
|
||||
perm_error_fmt = 'Permissions problem, target user may need '\
|
||||
'to be root or use sudo:\n {0}'
|
||||
if stderr.startswith('Permission denied'):
|
||||
|
@ -162,7 +162,7 @@ class Shell(object):
|
||||
'''
|
||||
Execute ssh-copy-id to plant the id file on the target
|
||||
'''
|
||||
stdout, stderr = self._run_cmd(self._copy_id_str_old())
|
||||
_, stderr, _ = self._run_cmd(self._copy_id_str_old())
|
||||
if stderr.startswith('Usage'):
|
||||
self._run_cmd(self._copy_id_str_new())
|
||||
|
||||
@ -208,9 +208,9 @@ class Shell(object):
|
||||
)
|
||||
|
||||
data = proc.communicate()
|
||||
return data
|
||||
return data[0], data[1], proc.returncode
|
||||
except Exception:
|
||||
return ('local', 'Unknown Error')
|
||||
return ('local', 'Unknown Error', None)
|
||||
|
||||
def _run_nb_cmd(self, cmd):
|
||||
'''
|
||||
@ -227,13 +227,14 @@ class Shell(object):
|
||||
time.sleep(0.1)
|
||||
out = proc.recv()
|
||||
err = proc.recv_err()
|
||||
rcode = proc.returncode
|
||||
if out is None and err is None:
|
||||
break
|
||||
if err:
|
||||
err = self.get_error(err)
|
||||
yield out, err
|
||||
yield out, err, rcode
|
||||
except Exception:
|
||||
yield ('', 'Unknown Error')
|
||||
yield ('', 'Unknown Error', None)
|
||||
|
||||
def exec_nb_cmd(self, cmd):
|
||||
'''
|
||||
@ -241,6 +242,7 @@ class Shell(object):
|
||||
'''
|
||||
r_out = []
|
||||
r_err = []
|
||||
rcode = None
|
||||
cmd = self._cmd_str(cmd)
|
||||
|
||||
logmsg = 'Executing non-blocking command: {0}'.format(cmd)
|
||||
@ -248,13 +250,13 @@ class Shell(object):
|
||||
logmsg = logmsg.replace(self.passwd, ('*' * len(self.passwd))[:6])
|
||||
log.debug(logmsg)
|
||||
|
||||
for out, err in self._run_nb_cmd(cmd):
|
||||
for out, err, rcode in self._run_nb_cmd(cmd):
|
||||
if out is not None:
|
||||
r_out.append(out)
|
||||
if err is not None:
|
||||
r_err.append(err)
|
||||
yield None, None
|
||||
yield ''.join(r_out), ''.join(r_err)
|
||||
yield None, None, None
|
||||
yield ''.join(r_out), ''.join(r_err), rcode
|
||||
|
||||
def exec_cmd(self, cmd):
|
||||
'''
|
||||
|
@ -54,10 +54,10 @@ class FunctionWrapper(object):
|
||||
''.join(arg_str),
|
||||
**self.kwargs
|
||||
)
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, _, _ = single.cmd_block()
|
||||
if stdout.startswith('deploy'):
|
||||
single.deploy()
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, _, _ = single.cmd_block()
|
||||
try:
|
||||
ret = json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
except ValueError:
|
||||
|
@ -74,7 +74,7 @@ def sls(mods, saltenv='base', test=None, exclude=None, env=None, **kwargs):
|
||||
single.shell.send(
|
||||
trans_tar,
|
||||
'/tmp/.salt/salt_state.tgz')
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, _ = single.cmd_block()
|
||||
return json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ def low(data):
|
||||
single.shell.send(
|
||||
trans_tar,
|
||||
'/tmp/.salt/salt_state.tgz')
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, _ = single.cmd_block()
|
||||
return json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ def high(data):
|
||||
single.shell.send(
|
||||
trans_tar,
|
||||
'/tmp/.salt/salt_state.tgz')
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, _ = single.cmd_block()
|
||||
return json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ def highstate(test=None, **kwargs):
|
||||
single.shell.send(
|
||||
trans_tar,
|
||||
'/tmp/.salt/salt_state.tgz')
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, _ = single.cmd_block()
|
||||
return json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
|
||||
|
||||
@ -223,7 +223,7 @@ def top(topfn, test=None, **kwargs):
|
||||
single.shell.send(
|
||||
trans_tar,
|
||||
'/tmp/.salt/salt_state.tgz')
|
||||
stdout, stderr = single.cmd_block()
|
||||
stdout, stderr, _ = single.cmd_block()
|
||||
return json.loads(stdout, object_hook=salt.utils.decode_dict)
|
||||
|
||||
|
||||
|
@ -333,7 +333,10 @@ class Cloud(object):
|
||||
|
||||
opts = self.opts.copy()
|
||||
multiprocessing_data = []
|
||||
for alias, drivers in self.opts['providers'].iteritems():
|
||||
|
||||
# Optimize Providers
|
||||
opts['providers'] = self._optimize_providers(opts['providers'])
|
||||
for alias, drivers in opts['providers'].iteritems():
|
||||
for driver, details in drivers.iteritems():
|
||||
fun = '{0}.{1}'.format(driver, query)
|
||||
if fun not in self.clouds:
|
||||
@ -383,7 +386,8 @@ class Cloud(object):
|
||||
self.__cached_provider_queries[query] = output
|
||||
return output
|
||||
|
||||
def get_running_by_names(self, names, query='list_nodes', cached=False):
|
||||
def get_running_by_names(self, names, query='list_nodes', cached=False,
|
||||
profile=None):
|
||||
if isinstance(names, basestring):
|
||||
names = [names]
|
||||
|
||||
@ -394,6 +398,16 @@ class Cloud(object):
|
||||
for driver, vms in drivers.iteritems():
|
||||
if driver not in handled_drivers:
|
||||
handled_drivers[driver] = alias
|
||||
# When a profile is specified, only return an instance
|
||||
# that matches the provider specified in the profile.
|
||||
# This solves the issues when many providers return the
|
||||
# same instance. For example there may be one provider for
|
||||
# each avaliablity zone in amazon in the same region, but
|
||||
# the search returns the same instance for each provider because
|
||||
# amazon returns all instances in a region, not avaliabilty zone.
|
||||
if profile:
|
||||
if alias not in self.opts['profiles'][profile]['provider'].split(':')[0]:
|
||||
continue
|
||||
|
||||
for vm_name, details in vms.iteritems():
|
||||
# XXX: The logic bellow can be removed once the aws driver
|
||||
@ -418,6 +432,43 @@ class Cloud(object):
|
||||
|
||||
return matches
|
||||
|
||||
def _optimize_providers(self, providers):
|
||||
'''
|
||||
Return an optimized mapping of available providers
|
||||
'''
|
||||
new_providers = {}
|
||||
provider_by_driver = {}
|
||||
|
||||
for alias, driver in providers.iteritems():
|
||||
for name, data in driver.iteritems():
|
||||
if name not in provider_by_driver:
|
||||
provider_by_driver[name] = {}
|
||||
|
||||
provider_by_driver[name][alias] = data
|
||||
|
||||
for driver, providers_data in provider_by_driver.iteritems():
|
||||
fun = '{0}.optimize_providers'.format(driver)
|
||||
if fun not in self.clouds:
|
||||
log.debug(
|
||||
'The {0!r} cloud driver is unable to be optimized.'.format(
|
||||
driver)
|
||||
)
|
||||
|
||||
for name, prov_data in providers_data.iteritems():
|
||||
if name not in new_providers:
|
||||
new_providers[name] = {}
|
||||
new_providers[name][driver] = prov_data
|
||||
continue
|
||||
|
||||
new_data = self.clouds[fun](providers_data)
|
||||
if new_data:
|
||||
for name, prov_data in new_data.iteritems():
|
||||
if name not in new_providers:
|
||||
new_providers[name] = {}
|
||||
new_providers[name][driver] = prov_data
|
||||
|
||||
return new_providers
|
||||
|
||||
def location_list(self, lookup='all'):
|
||||
'''
|
||||
Return a mapping of all location data for available providers
|
||||
|
@ -237,7 +237,8 @@ class SaltCloud(parsers.SaltCloudParser):
|
||||
matching = mapper.delete_map(query='list_nodes')
|
||||
else:
|
||||
matching = mapper.get_running_by_names(
|
||||
self.config.get('names', ())
|
||||
self.config.get('names', ()),
|
||||
profile=self.options.profile
|
||||
)
|
||||
|
||||
if not matching:
|
||||
|
@ -236,6 +236,45 @@ def _xml_to_dict(xmltree):
|
||||
return xmldict
|
||||
|
||||
|
||||
def optimize_providers(providers):
|
||||
'''
|
||||
Return an optimized list of providers.
|
||||
|
||||
We want to reduce the duplication of querying
|
||||
the same region.
|
||||
|
||||
If a provider is using the same credentials for the same region
|
||||
the same data will be returned for each provider, thus causing
|
||||
un-wanted duplicate data and API calls to EC2.
|
||||
|
||||
'''
|
||||
tmp_providers = {}
|
||||
optimized_providers = {}
|
||||
|
||||
for name, data in providers.iteritems():
|
||||
if 'location' not in data:
|
||||
data['location'] = DEFAULT_LOCATION
|
||||
|
||||
if data['location'] not in tmp_providers:
|
||||
tmp_providers[data['location']] = {}
|
||||
|
||||
creds = (data['id'], data['key'])
|
||||
if creds not in tmp_providers[data['location']]:
|
||||
tmp_providers[data['location']][creds] = {'name': name,
|
||||
'data': data,
|
||||
}
|
||||
|
||||
for location, tmp_data in tmp_providers.iteritems():
|
||||
for creds, data in tmp_data.iteritems():
|
||||
_id, _key = creds
|
||||
_name = data['name']
|
||||
_data = data['data']
|
||||
if _name not in optimized_providers:
|
||||
optimized_providers[_name] = _data
|
||||
|
||||
return optimized_providers
|
||||
|
||||
|
||||
def query(params=None, setname=None, requesturl=None, location=None,
|
||||
return_url=False, return_root=False):
|
||||
|
||||
|
@ -1722,6 +1722,8 @@ def get_id(root_dir=None, minion_id=False, cache=True):
|
||||
with salt.utils.fopen('/etc/hosts') as hfl:
|
||||
for line in hfl:
|
||||
names = line.split()
|
||||
if not names:
|
||||
continue
|
||||
ip_ = names.pop(0)
|
||||
if ip_.startswith('127.'):
|
||||
for name in names:
|
||||
|
@ -237,9 +237,6 @@ class Auth(object):
|
||||
public key to encrypt the AES key sent back form the master.
|
||||
'''
|
||||
payload = {}
|
||||
key = self.get_keys()
|
||||
tmp_pub = salt.utils.mkstemp()
|
||||
key.save_pub_key(tmp_pub)
|
||||
payload['enc'] = 'clear'
|
||||
payload['load'] = {}
|
||||
payload['load']['cmd'] = '_auth'
|
||||
@ -251,9 +248,8 @@ class Auth(object):
|
||||
payload['load']['token'] = pub.public_encrypt(self.token, RSA.pkcs1_oaep_padding)
|
||||
except Exception:
|
||||
pass
|
||||
with salt.utils.fopen(tmp_pub, 'r') as fp_:
|
||||
with salt.utils.fopen(self.pub_path, 'r') as fp_:
|
||||
payload['load']['pub'] = fp_.read()
|
||||
os.remove(tmp_pub)
|
||||
return payload
|
||||
|
||||
def decrypt_aes(self, payload, master_pub=True):
|
||||
|
@ -28,6 +28,12 @@ class SaltMasterError(SaltException):
|
||||
'''
|
||||
|
||||
|
||||
class SaltSyndicMasterError(SaltException):
|
||||
'''
|
||||
Problem while proxying a request in the syndication master
|
||||
'''
|
||||
|
||||
|
||||
class MasterExit(SystemExit):
|
||||
'''
|
||||
Rise when the master exits
|
||||
|
@ -268,15 +268,17 @@ def ssh_wrapper(opts, functions=None):
|
||||
return load.gen_functions(pack)
|
||||
|
||||
|
||||
def render(opts, functions):
|
||||
def render(opts, functions, states=None):
|
||||
'''
|
||||
Returns the render modules
|
||||
'''
|
||||
load = _create_loader(
|
||||
opts, 'renderers', 'render', ext_type_dirs='render_dirs'
|
||||
)
|
||||
pack = {'name': '__salt__',
|
||||
'value': functions}
|
||||
pack = [{'name': '__salt__',
|
||||
'value': functions}]
|
||||
if states:
|
||||
pack.append({'name': '__states__', 'value': states})
|
||||
rend = load.filter_func('render', pack)
|
||||
if not check_render_pipe_str(opts['renderer'], rend):
|
||||
err = ('The renderer {0} is unavailable, this error is often because '
|
||||
|
@ -411,7 +411,21 @@ class Publisher(multiprocessing.Process):
|
||||
# SIGUSR1 gracefully so we don't choke and die horribly
|
||||
try:
|
||||
package = pull_sock.recv()
|
||||
pub_sock.send(package)
|
||||
unpacked_package = salt.payload.unpackage(package)
|
||||
payload = unpacked_package['payload']
|
||||
|
||||
# if you have a specific topic list, use that
|
||||
if 'topic_lst' in unpacked_package:
|
||||
for topic in unpacked_package['topic_lst']:
|
||||
# zmq filters are substring match, hash the topic
|
||||
# to avoid collisions
|
||||
htopic = hashlib.sha1(topic).hexdigest()
|
||||
pub_sock.send(htopic, flags=zmq.SNDMORE)
|
||||
pub_sock.send(payload)
|
||||
# otherwise its a broadcast
|
||||
else:
|
||||
pub_sock.send('broadcast', flags=zmq.SNDMORE)
|
||||
pub_sock.send(payload)
|
||||
except zmq.ZMQError as exc:
|
||||
if exc.errno == errno.EINTR:
|
||||
continue
|
||||
@ -2638,7 +2652,14 @@ class ClearFuncs(object):
|
||||
os.path.join(self.opts['sock_dir'], 'publish_pull.ipc')
|
||||
)
|
||||
pub_sock.connect(pull_uri)
|
||||
pub_sock.send(self.serial.dumps(payload))
|
||||
|
||||
int_payload = {'payload': self.serial.dumps(payload)}
|
||||
|
||||
# add some targeting stuff for lists only (for now)
|
||||
if load['tgt_type'] == 'list':
|
||||
int_payload['topic_lst'] = load['tgt']
|
||||
|
||||
pub_sock.send(self.serial.dumps(int_payload))
|
||||
return {
|
||||
'enc': 'clear',
|
||||
'load': {
|
||||
|
@ -56,7 +56,8 @@ except ImportError:
|
||||
# Import salt libs
|
||||
from salt.exceptions import (
|
||||
AuthenticationError, CommandExecutionError, CommandNotFoundError,
|
||||
SaltInvocationError, SaltReqTimeoutError, SaltClientError, SaltSystemExit
|
||||
SaltInvocationError, SaltReqTimeoutError, SaltClientError,
|
||||
SaltSystemExit, SaltSyndicMasterError
|
||||
)
|
||||
import salt.client
|
||||
import salt.crypt
|
||||
@ -607,6 +608,10 @@ class Minion(MinionBase):
|
||||
|
||||
self.grains_cache = self.opts['grains']
|
||||
|
||||
# store your hexid to subscribe to zmq, hash since zmq filters are prefix
|
||||
# matches this way we can avoid collisions
|
||||
self.hexid = hashlib.sha1(self.opts['id']).hexdigest()
|
||||
|
||||
if 'proxy' in self.opts['pillar']:
|
||||
log.debug('I am {0} and I need to start some proxies for {0}'.format(self.opts['id'],
|
||||
self.opts['pillar']['proxy']))
|
||||
@ -1129,7 +1134,8 @@ class Minion(MinionBase):
|
||||
)
|
||||
|
||||
def _setsockopts(self):
|
||||
self.socket.setsockopt(zmq.SUBSCRIBE, '')
|
||||
self.socket.setsockopt(zmq.SUBSCRIBE, 'broadcast')
|
||||
self.socket.setsockopt(zmq.SUBSCRIBE, self.hexid)
|
||||
self.socket.setsockopt(zmq.IDENTITY, self.opts['id'])
|
||||
self._set_ipv4only()
|
||||
self._set_reconnect_ivl_max()
|
||||
@ -1195,6 +1201,20 @@ class Minion(MinionBase):
|
||||
).compile_pillar()
|
||||
self.module_refresh()
|
||||
|
||||
def environ_setenv(self, package):
|
||||
'''
|
||||
Set the salt-minion main process environment according to
|
||||
the data contained in the minion event data
|
||||
'''
|
||||
tag, data = salt.utils.event.MinionEvent.unpack(package)
|
||||
environ = data.get('environ', None)
|
||||
if environ is None:
|
||||
return False
|
||||
false_unsets = data.get('false_unsets', False)
|
||||
clear_all = data.get('clear_all', False)
|
||||
import salt.modules.environ as mod_environ
|
||||
return mod_environ.setenv(environ, false_unsets, clear_all)
|
||||
|
||||
def clean_die(self, signum, frame):
|
||||
'''
|
||||
Python does not handle the SIGTERM cleanly, if it is signaled exit
|
||||
@ -1313,6 +1333,8 @@ class Minion(MinionBase):
|
||||
if self.grains_cache != self.opts['grains']:
|
||||
self.pillar_refresh()
|
||||
self.grains_cache = self.opts['grains']
|
||||
elif package.startswith('environ_setenv'):
|
||||
self.environ_setenv(package)
|
||||
elif package.startswith('fire_master'):
|
||||
tag, data = salt.utils.event.MinionEvent.unpack(package)
|
||||
log.debug('Forwarding master event tag={tag}'.format(tag=data['tag']))
|
||||
@ -1381,7 +1403,12 @@ class Minion(MinionBase):
|
||||
|
||||
def _do_socket_recv(self, socks):
|
||||
if socks.get(self.socket) == zmq.POLLIN:
|
||||
payload = self.serial.loads(self.socket.recv(zmq.NOBLOCK))
|
||||
# topic filtering is done at the zmq level, so we just strip it
|
||||
recv_str = self.socket.recv(zmq.NOBLOCK)
|
||||
# if you have a header, then you have another one coming down the pipe
|
||||
if recv_str in (self.hexid, 'broadcast'):
|
||||
recv_str = self.socket.recv(zmq.NOBLOCK)
|
||||
payload = self.serial.loads(recv_str)
|
||||
log.trace('Handling payload')
|
||||
self._handle_payload(payload)
|
||||
|
||||
@ -1500,6 +1527,8 @@ class Syndic(Minion):
|
||||
# Share the poller with the event object
|
||||
self.poller = self.local.event.poller
|
||||
self.socket = self.context.socket(zmq.SUB)
|
||||
# no filters for syndication masters, unless we want to maintain a
|
||||
# list of all connected minions and update the filter
|
||||
self.socket.setsockopt(zmq.SUBSCRIBE, '')
|
||||
self.socket.setsockopt(zmq.IDENTITY, self.opts['id'])
|
||||
if hasattr(zmq, 'RECONNECT_IVL_MAX'):
|
||||
@ -1577,7 +1606,18 @@ class Syndic(Minion):
|
||||
|
||||
def _process_cmd_socket(self):
|
||||
try:
|
||||
payload = self.serial.loads(self.socket.recv(zmq.NOBLOCK))
|
||||
messages = self.socket.recv_multipart(zmq.NOBLOCK)
|
||||
messages_len = len(messages)
|
||||
idx = None
|
||||
if messages_len == 1:
|
||||
idx = 0
|
||||
elif messages_len == 2:
|
||||
idx = 1
|
||||
else:
|
||||
raise SaltSyndicMasterError('Syndication master recieved message of invalid len ({0}/2)'.format(messages_len))
|
||||
|
||||
payload = self.serial.loads(messages[idx])
|
||||
|
||||
except zmq.ZMQError as e:
|
||||
# Swallow errors for bad wakeups or signals needing processing
|
||||
if e.errno != errno.EAGAIN and e.errno != errno.EINTR:
|
||||
|
@ -418,6 +418,7 @@ def _run(cmd,
|
||||
except TimedProcTimeoutError as exc:
|
||||
ret['stdout'] = str(exc)
|
||||
ret['stderr'] = ''
|
||||
ret['retcode'] = None
|
||||
ret['pid'] = proc.process.pid
|
||||
# ok return code for timeouts?
|
||||
ret['retcode'] = 1
|
||||
@ -679,6 +680,8 @@ def run_stdout(cmd,
|
||||
log.log(lvl, 'stdout: {0}'.format(ret['stdout']))
|
||||
if ret['stderr']:
|
||||
log.log(lvl, 'stderr: {0}'.format(ret['stderr']))
|
||||
if ret['retcode']:
|
||||
log.log(lvl, 'retcode: {0}'.format(ret['retcode']))
|
||||
return ret['stdout']
|
||||
|
||||
|
||||
@ -757,6 +760,8 @@ def run_stderr(cmd,
|
||||
log.log(lvl, 'stdout: {0}'.format(ret['stdout']))
|
||||
if ret['stderr']:
|
||||
log.log(lvl, 'stderr: {0}'.format(ret['stderr']))
|
||||
if ret['retcode']:
|
||||
log.log(lvl, 'retcode: {0}'.format(ret['retcode']))
|
||||
return ret['stderr']
|
||||
|
||||
|
||||
@ -835,6 +840,8 @@ def run_all(cmd,
|
||||
log.log(lvl, 'stdout: {0}'.format(ret['stdout']))
|
||||
if ret['stderr']:
|
||||
log.log(lvl, 'stderr: {0}'.format(ret['stderr']))
|
||||
if ret['retcode']:
|
||||
log.log(lvl, 'retcode: {0}'.format(ret['retcode']))
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -23,7 +23,7 @@ def _encode(string):
|
||||
|
||||
|
||||
def _cron_id(cron):
|
||||
'''SAFEBELT, Oanly setted if we really have an identifier'''
|
||||
'''SAFETYBELT, Only set if we really have an identifier'''
|
||||
cid = None
|
||||
if cron['identifier']:
|
||||
cid = cron['identifier']
|
||||
@ -36,11 +36,11 @@ def _cron_id(cron):
|
||||
def _cron_matched(cron, cmd, identifier=None):
|
||||
'''Check if:
|
||||
- we find a cron with same cmd, old state behavior
|
||||
- but also be enough smart to remove states changed crons where we do
|
||||
not removed priorly by a cron.absent by matching on the providen
|
||||
- but also be smart enough to remove states changed crons where we do
|
||||
not removed priorly by a cron.absent by matching on the provided
|
||||
identifier.
|
||||
We assure retrocompatiblity by only checking on identifier if
|
||||
and only an identifier was set on the serialized crontab
|
||||
and only if an identifier was set on the serialized crontab
|
||||
'''
|
||||
ret, id_matched = False, None
|
||||
cid = _cron_id(cron)
|
||||
|
@ -421,8 +421,8 @@ def logs(container, *args, **kwargs):
|
||||
status = base_status.copy()
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.logs(_get_container_infos(container)['id'])
|
||||
valid(status, id=container, out=info)
|
||||
container_logs = client.logs(_get_container_infos(container)['id'])
|
||||
valid(status, id=container, out=container_logs)
|
||||
except Exception:
|
||||
invalid(status, id=container, out=traceback.format_exc())
|
||||
return status
|
||||
@ -462,7 +462,7 @@ def commit(container,
|
||||
client = _get_client()
|
||||
try:
|
||||
container = _get_container_infos(container)['id']
|
||||
info = client.commit(
|
||||
commit_info = client.commit(
|
||||
container,
|
||||
repository=repository,
|
||||
tag=tag,
|
||||
@ -471,14 +471,14 @@ def commit(container,
|
||||
conf=conf)
|
||||
found = False
|
||||
for k in ('Id', 'id', 'ID'):
|
||||
if k in info:
|
||||
if k in commit_info:
|
||||
found = True
|
||||
image_id = info[k]
|
||||
image_id = commit_info[k]
|
||||
if not found:
|
||||
raise Exception('Invalid commit return')
|
||||
image = _get_image_infos(image_id)['id']
|
||||
comment = 'Image {0} created from {1}'.format(image, container)
|
||||
valid(status, id=image, out=info, comment=comment)
|
||||
valid(status, id=image, out=commit_info, comment=comment)
|
||||
except Exception:
|
||||
invalid(status, id=container, out=traceback.format_exc())
|
||||
return status
|
||||
@ -500,8 +500,8 @@ def diff(container, *args, **kwargs):
|
||||
status = base_status.copy()
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.diff(_get_container_infos(container)['id'])
|
||||
valid(status, id=container, out=info)
|
||||
container_diff = client.diff(_get_container_infos(container)['id'])
|
||||
valid(status, id=container, out=container_diff)
|
||||
except Exception:
|
||||
invalid(status, id=container, out=traceback.format_exc())
|
||||
return status
|
||||
@ -622,7 +622,7 @@ def create_container(image,
|
||||
mounted = parts[0]
|
||||
mountpoints[mountpoint] = {}
|
||||
binds[mounted] = mountpoint
|
||||
info = client.create_container(
|
||||
container_info = client.create_container(
|
||||
image=image,
|
||||
command=command,
|
||||
hostname=hostname,
|
||||
@ -638,12 +638,12 @@ def create_container(image,
|
||||
volumes_from=volumes_from,
|
||||
name=name,
|
||||
)
|
||||
container = info['Id']
|
||||
container = container_info['Id']
|
||||
callback = valid
|
||||
comment = 'Container created'
|
||||
out = {
|
||||
'info': _get_container_infos(container),
|
||||
'out': info
|
||||
'out': container_info
|
||||
}
|
||||
return callback(status, id=container, comment=comment, out=out)
|
||||
except Exception:
|
||||
@ -664,8 +664,8 @@ def version(*args, **kwargs):
|
||||
status = base_status.copy()
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.version()
|
||||
valid(status, out=info)
|
||||
docker_version = client.version()
|
||||
valid(status, out=docker_version)
|
||||
except Exception:
|
||||
invalid(status, out=traceback.format_exc())
|
||||
return status
|
||||
@ -687,8 +687,8 @@ def info(*args, **kwargs):
|
||||
status = base_status.copy()
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.info()
|
||||
valid(status, out=info)
|
||||
version_info = client.info()
|
||||
valid(status, out=version_info)
|
||||
except Exception:
|
||||
invalid(status, out=traceback.format_exc())
|
||||
return status
|
||||
@ -715,10 +715,10 @@ def port(container, private_port, *args, **kwargs):
|
||||
status = base_status.copy()
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.port(
|
||||
port_info = client.port(
|
||||
_get_container_infos(container)['id'],
|
||||
port)
|
||||
valid(status, id=container, out=info)
|
||||
valid(status, id=container, out=port_info)
|
||||
except Exception:
|
||||
invalid(status, id=container, out=traceback.format_exc())
|
||||
return status
|
||||
@ -1251,7 +1251,7 @@ def import_image(src, repo, tag=None, *args, **kwargs):
|
||||
try:
|
||||
ret = client.import_image(src, repository=repo, tag=tag)
|
||||
if ret:
|
||||
logs, info = _parse_image_multilogs_string(ret, repo)
|
||||
logs, _info = _parse_image_multilogs_string(ret, repo)
|
||||
_create_image_assemble_error_status(status, ret, logs)
|
||||
if status['status'] is not False:
|
||||
infos = _get_image_infos(logs[0]['status'])
|
||||
|
@ -6,14 +6,12 @@ of the current salt process.
|
||||
|
||||
# Import python libs
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Import salt libs
|
||||
from salt._compat import string_types
|
||||
from salt.exceptions import SaltException
|
||||
|
||||
__func_alias__ = {
|
||||
'set_': 'set'
|
||||
}
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
@ -23,51 +21,229 @@ def __virtual__():
|
||||
return True
|
||||
|
||||
|
||||
def set_(environ):
|
||||
def setval(key, val, false_unsets=False):
|
||||
'''
|
||||
Set the salt process environment variables.
|
||||
Set a single salt process environment variable. Returns True
|
||||
on success.
|
||||
|
||||
Accepts a dict 'environ'. Each top-level key of the dict
|
||||
are the names of the environment variables to set.
|
||||
The value to set must be a string.
|
||||
key
|
||||
The environment key to set. Must be a string.
|
||||
|
||||
val
|
||||
The value to set. Must be a string or False. Refer to the
|
||||
'false_unsets' parameter for behavior when set to False.
|
||||
|
||||
false_unsets
|
||||
If val is False and false_unsets is True, then the key will be
|
||||
removed from the salt processes environment dict entirely.
|
||||
If val is False and false_unsets is not True, then the key's
|
||||
value will be set to an empty string.
|
||||
Default: False.
|
||||
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.set '{"foo": "bar", "baz": "quux"}'
|
||||
salt '*' environ.setval foo bar
|
||||
salt '*' environ.setval baz val=False false_unsets=True
|
||||
'''
|
||||
if not isinstance(key, string_types):
|
||||
log.debug(
|
||||
'{0}: "key" argument is not a string type: {1!r}'
|
||||
.format(__name__, key)
|
||||
)
|
||||
if val is False:
|
||||
if false_unsets is True:
|
||||
try:
|
||||
os.environ.pop(key, None)
|
||||
return None
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'{0}: Exception occurred when unsetting '
|
||||
'environ key "{1!r}": {2!r}'
|
||||
.format(__name__, key, exc)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
val = ''
|
||||
if isinstance(val, string_types):
|
||||
try:
|
||||
os.environ[key] = val
|
||||
return os.environ[key]
|
||||
except Exception as exc:
|
||||
log.error(
|
||||
'{0}: Exception occurred when setting'
|
||||
'environ key "{1!r}": {2!r}'
|
||||
.format(__name__, key, exc)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
log.debug(
|
||||
'{0}: "val" argument for key "{1!r}" is not a string '
|
||||
'or False: {2!r}'
|
||||
.format(__name__, key, val)
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def setenv(environ, false_unsets=False, clear_all=False, update_minion=False):
|
||||
'''
|
||||
Set multiple salt process environment variables from a dict.
|
||||
Returns a dict.
|
||||
|
||||
environ
|
||||
Must be a dict. The top-level keys of the dict are the names
|
||||
of the environment variables to set. Each key's value must be
|
||||
a string or False. Refer to the 'false_unsets' parameter for
|
||||
behavior when a value set to False.
|
||||
|
||||
false_unsets
|
||||
If a key's value is False and false_unsets is True, then the
|
||||
key will be removed from the salt processes environment dict
|
||||
entirely. If a key's value is Flase and false_unsets is not
|
||||
True, then the key's value will be set to an empty string.
|
||||
Default: False
|
||||
|
||||
clear_all
|
||||
USE WITH CAUTION! This option can unset environment variables
|
||||
needed for salt to function properly.
|
||||
If clear_all is True, then any environment variables not
|
||||
defined in the environ dict will be deleted.
|
||||
Default: False
|
||||
|
||||
update_minion
|
||||
If True, apply these environ changes to the main salt-minion
|
||||
process. If False, the environ changes will only affect the
|
||||
current salt subprocess.
|
||||
Default: False
|
||||
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.setenv '{"foo": "bar", "baz": "quux"}'
|
||||
salt '*' environ.setenv '{"a": "b", "c": False}' false_unsets=True
|
||||
'''
|
||||
ret = {}
|
||||
if not isinstance(environ, dict):
|
||||
raise SaltException('The "environ" argument variable must be a dict')
|
||||
try:
|
||||
for key, val in environ.items():
|
||||
if not isinstance(val, string_types):
|
||||
raise SaltException(
|
||||
'The value of "environ" keys must be string type'
|
||||
log.debug(
|
||||
'{0}: "environ" argument is not a dict: {1!r}'
|
||||
.format(__name__, environ)
|
||||
)
|
||||
os.environ[key] = val
|
||||
ret[key] = os.environ[key]
|
||||
except Exception as exc:
|
||||
raise SaltException(exc)
|
||||
return False
|
||||
if clear_all is True:
|
||||
# Unset any keys not defined in 'environ' dict supplied by user
|
||||
to_unset = [key for key in os.environ.keys() if key not in environ]
|
||||
for key in to_unset:
|
||||
ret[key] = setval(key, False, false_unsets)
|
||||
for key, val in environ.items():
|
||||
if isinstance(val, string_types):
|
||||
ret[key] = setval(key, val)
|
||||
elif val is False:
|
||||
ret[key] = setval(key, val, false_unsets)
|
||||
else:
|
||||
log.debug(
|
||||
'{0}: "val" argument for key "{1!r}" is not a string '
|
||||
'or False: {2!r}'
|
||||
.format(__name__, key, val)
|
||||
)
|
||||
return False
|
||||
|
||||
if update_minion is True:
|
||||
__salt__['event.fire']({'environ': environ,
|
||||
'false_unsets': false_unsets,
|
||||
'clear_all': clear_all
|
||||
},
|
||||
'environ_setenv')
|
||||
return ret
|
||||
|
||||
|
||||
def get(keys):
|
||||
def get(key, default=''):
|
||||
'''
|
||||
Get the salt process environment variables.
|
||||
Get a single salt process environment variable.
|
||||
|
||||
key
|
||||
String used as the key for environment lookup.
|
||||
|
||||
default
|
||||
If the key is not found in the enironment, return this value.
|
||||
Default: ''
|
||||
|
||||
'keys' can be either a string or a list of strings that will
|
||||
be used as the keys for environment lookup.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.get foo
|
||||
salt '*' environ.get '[foo, baz]'
|
||||
salt '*' environ.get baz default=False
|
||||
'''
|
||||
if not isinstance(key, string_types):
|
||||
log.debug(
|
||||
'{0}: "key" argument is not a string type: {1!r}'
|
||||
.format(__name__, key)
|
||||
)
|
||||
return False
|
||||
return os.environ.get(key, default)
|
||||
|
||||
|
||||
def has_value(key, value=None):
|
||||
'''
|
||||
Determine whether the key exists in the current salt process
|
||||
environment dictionary. Optionally compare the current value
|
||||
of the environment against the supplied value string.
|
||||
|
||||
key
|
||||
Must be a string. Used as key for environment lookup.
|
||||
|
||||
value:
|
||||
Optional. If key exists in the environment, compare the
|
||||
current value with this value. Return True if they are equal.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.has_value foo
|
||||
'''
|
||||
if not isinstance(key, string_types):
|
||||
log.debug(
|
||||
'{0}: "key" argument is not a string type: {1!r}'
|
||||
.format(__name__, key)
|
||||
)
|
||||
return False
|
||||
try:
|
||||
cur_val = os.environ[key]
|
||||
if value is not None:
|
||||
if cur_val == value:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except KeyError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def item(keys, default=''):
|
||||
'''
|
||||
Get one or more salt process environment variables.
|
||||
Returns a dict.
|
||||
|
||||
keys
|
||||
Either a string or a list of strings that will be used as the
|
||||
keys for environment lookup.
|
||||
|
||||
default
|
||||
If the key is not found in the enironment, return this value.
|
||||
Default: ''
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.item foo
|
||||
salt '*' environ.item '[foo, baz]' default=None
|
||||
'''
|
||||
ret = {}
|
||||
key_list = []
|
||||
@ -76,15 +252,16 @@ def get(keys):
|
||||
elif isinstance(keys, list):
|
||||
key_list = keys
|
||||
else:
|
||||
raise SaltException(
|
||||
'The "keys" argument variable must be string or list.'
|
||||
log.debug(
|
||||
'{0}: "keys" argument is not a string or list type: {1!r}'
|
||||
.format(__name__, keys)
|
||||
)
|
||||
for key in key_list:
|
||||
ret[key] = os.environ[key]
|
||||
ret[key] = os.environ.get(key, default)
|
||||
return ret
|
||||
|
||||
|
||||
def get_all():
|
||||
def items():
|
||||
'''
|
||||
Return a dict of the entire environment set for the salt process
|
||||
|
||||
@ -92,6 +269,6 @@ def get_all():
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' environ.get:all
|
||||
salt '*' environ.items
|
||||
'''
|
||||
return dict(os.environ)
|
||||
|
@ -84,7 +84,7 @@ def bootstrap(location, size, fmt):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' qemu_nbd.bootstrap /srv/salt-images/host.qcow 4096 qcow2
|
||||
salt '*' img.bootstrap /srv/salt-images/host.qcow 4096 qcow2
|
||||
'''
|
||||
location = __salt__['img.make_image'](location, size, fmt)
|
||||
if not location:
|
||||
|
@ -338,7 +338,7 @@ def chgroups(name, groups, append=False):
|
||||
if isinstance(groups, string_types):
|
||||
groups = groups.split(',')
|
||||
|
||||
bad_groups = any(salt.utils.contains_whitespace(x) for x in groups)
|
||||
bad_groups = [x for x in groups if salt.utils.contains_whitespace(x)]
|
||||
if bad_groups:
|
||||
raise SaltInvocationError(
|
||||
'Invalid group name(s): {0}'.format(', '.join(bad_groups))
|
||||
|
353
salt/modules/macports.py
Normal file
353
salt/modules/macports.py
Normal file
@ -0,0 +1,353 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Support for MacPorts under MacOSX
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import copy
|
||||
import logging
|
||||
import re
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
LIST_ACTIVE_ONLY = True
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Confine this module to Mac OS with MacPorts.
|
||||
'''
|
||||
|
||||
if salt.utils.which('port') and __grains__['os'] == 'MacOS':
|
||||
return 'pkg'
|
||||
return False
|
||||
|
||||
|
||||
def _list(query=""):
|
||||
ret = {}
|
||||
cmd = 'port list %s' % query
|
||||
for line in __salt__['cmd.run'](cmd).splitlines():
|
||||
try:
|
||||
name, version_num, category = re.split(r"\s+", line.lstrip())[0:3]
|
||||
version_num = version_num[1:]
|
||||
except ValueError:
|
||||
continue
|
||||
ret[name] = version_num
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def list_pkgs(versions_as_list=False, **kwargs):
|
||||
'''
|
||||
List the packages currently installed in a dict::
|
||||
|
||||
{'<package_name>': '<version>'}
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.list_pkgs
|
||||
'''
|
||||
versions_as_list = salt.utils.is_true(versions_as_list)
|
||||
# 'removed' not yet implemented or not applicable
|
||||
if salt.utils.is_true(kwargs.get('removed')):
|
||||
return {}
|
||||
|
||||
if 'pkg.list_pkgs' in __context__:
|
||||
if versions_as_list:
|
||||
return __context__['pkg.list_pkgs']
|
||||
else:
|
||||
ret = copy.deepcopy(__context__['pkg.list_pkgs'])
|
||||
__salt__['pkg_resource.stringify'](ret)
|
||||
return ret
|
||||
|
||||
ret = {}
|
||||
cmd = 'port installed'
|
||||
for line in __salt__['cmd.run'](cmd).splitlines():
|
||||
try:
|
||||
name, version_num, active = re.split(r"\s+", line.lstrip())[0:3]
|
||||
version_num = version_num[1:]
|
||||
except ValueError:
|
||||
continue
|
||||
if not LIST_ACTIVE_ONLY or active == "(active)":
|
||||
__salt__['pkg_resource.add_pkg'](ret, name, version_num)
|
||||
|
||||
__salt__['pkg_resource.sort_pkglist'](ret)
|
||||
__context__['pkg.list_pkgs'] = copy.deepcopy(ret)
|
||||
if not versions_as_list:
|
||||
__salt__['pkg_resource.stringify'](ret)
|
||||
return ret
|
||||
|
||||
|
||||
def version(*names, **kwargs):
|
||||
'''
|
||||
Returns a string representing the package version or an empty string if not
|
||||
installed. If more than one package name is specified, a dict of
|
||||
name/version pairs is returned.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.version <package name>
|
||||
salt '*' pkg.version <package1> <package2> <package3>
|
||||
'''
|
||||
return __salt__['pkg_resource.version'](*names, **kwargs)
|
||||
|
||||
|
||||
def latest_version(*names, **kwargs):
|
||||
'''
|
||||
Return the latest version of the named package available for upgrade or
|
||||
installation
|
||||
|
||||
Options:
|
||||
|
||||
refresh
|
||||
Update ports with ``port selfupdate``
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.latest_version <package name>
|
||||
salt '*' pkg.latest_version <package1> <package2> <package3>
|
||||
'''
|
||||
|
||||
if salt.utils.is_true(kwargs.get('refresh', True)):
|
||||
refresh_db()
|
||||
|
||||
available = _list(" ".join(names)) or {}
|
||||
installed = __salt__['pkg.list_pkgs']() or {}
|
||||
|
||||
ret = {}
|
||||
|
||||
for k, v in available.items():
|
||||
if not k in installed or salt.utils.compare_versions(ver1=installed[k], oper='<', ver2=v):
|
||||
ret[k] = v
|
||||
else:
|
||||
ret[k] = ""
|
||||
|
||||
# Return a string if only one package name passed
|
||||
if len(names) == 1:
|
||||
return ret[names[0]]
|
||||
|
||||
return ret
|
||||
|
||||
# available_version is being deprecated
|
||||
available_version = latest_version
|
||||
|
||||
|
||||
def remove(name=None, pkgs=None, **kwargs):
|
||||
'''
|
||||
Removes packages with ``port uninstall``.
|
||||
|
||||
name
|
||||
The name of the package to be deleted.
|
||||
|
||||
|
||||
Multiple Package Options:
|
||||
|
||||
pkgs
|
||||
A list of packages to delete. Must be passed as a python list. The
|
||||
``name`` parameter will be ignored if this option is passed.
|
||||
|
||||
.. versionadded:: 0.16.0
|
||||
|
||||
|
||||
Returns a dict containing the changes.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.remove <package name>
|
||||
salt '*' pkg.remove <package1>,<package2>,<package3>
|
||||
salt '*' pkg.remove pkgs='["foo", "bar"]'
|
||||
'''
|
||||
pkg_params = __salt__['pkg_resource.parse_targets'](name,
|
||||
pkgs,
|
||||
**kwargs)[0]
|
||||
old = list_pkgs()
|
||||
targets = [x for x in pkg_params if x in old]
|
||||
if not targets:
|
||||
return {}
|
||||
cmd = 'port uninstall {0}'.format(' '.join(targets))
|
||||
__salt__['cmd.run_all'](cmd)
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
return __salt__['pkg_resource.find_changes'](old, new)
|
||||
|
||||
|
||||
def install(name=None, refresh=False, pkgs=None, **kwargs):
|
||||
'''
|
||||
Install the passed package(s) with ``port install``
|
||||
|
||||
name
|
||||
The name of the formula to be installed. Note that this parameter is
|
||||
ignored if "pkgs" is passed.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.install <package name>
|
||||
|
||||
version
|
||||
Specify a version to pkg to install. Ignored if pkgs is sepcified.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.install <package name>
|
||||
salt '*' pkg.install git-core version='1.8.5.5'
|
||||
|
||||
variant
|
||||
Specify a variant to pkg to install. Ignored if pkgs is sepcified.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.install <package name>
|
||||
salt '*' pkg.install git-core version='1.8.5.5' variant='+credential_osxkeychain+doc+pcre'
|
||||
|
||||
Multiple Package Installation Options:
|
||||
|
||||
pkgs
|
||||
A list of formulas to install. Must be passed as a python list.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.install pkgs='["foo","bar"]'
|
||||
salt '*' pkg.install pkgs='["foo@1.2","bar"]'
|
||||
salt '*' pkg.install pkgs='["foo@1.2+ssl","bar@2.3"]'
|
||||
|
||||
|
||||
Returns a dict containing the new package names and versions::
|
||||
|
||||
{'<package>': {'old': '<old-version>',
|
||||
'new': '<new-version>'}}
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.install 'package package package'
|
||||
'''
|
||||
pkg_params, pkg_type = \
|
||||
__salt__['pkg_resource.parse_targets'](name,
|
||||
pkgs,
|
||||
{})
|
||||
|
||||
if salt.utils.is_true(refresh):
|
||||
refresh_db()
|
||||
|
||||
# Handle version kwarg for a single package target
|
||||
if pkgs is None:
|
||||
version_num = kwargs.get('version')
|
||||
variant_spec = kwargs.get('variant')
|
||||
spec = None
|
||||
|
||||
if version_num:
|
||||
spec = (spec or "") + "@" + version_num
|
||||
|
||||
if variant_spec:
|
||||
spec = (spec or "") + variant_spec
|
||||
|
||||
pkg_params = {name: spec}
|
||||
|
||||
if pkg_params is None or len(pkg_params) == 0:
|
||||
return {}
|
||||
|
||||
formulas_array = []
|
||||
for pname, pparams in pkg_params.items():
|
||||
formulas_array.append(pname + (pparams or ""))
|
||||
|
||||
formulas = " ".join(formulas_array)
|
||||
|
||||
old = list_pkgs()
|
||||
cmd = 'port install {0}'.format(formulas)
|
||||
|
||||
__salt__['cmd.run'](cmd)
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
return __salt__['pkg_resource.find_changes'](old, new)
|
||||
|
||||
|
||||
def list_upgrades(refresh=True):
|
||||
'''
|
||||
Check whether or not an upgrade is available for all packages
|
||||
|
||||
Options:
|
||||
|
||||
refresh
|
||||
Update ports with ``port selfupdate``
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.list_upgrades
|
||||
'''
|
||||
|
||||
if refresh:
|
||||
refresh_db()
|
||||
return _list("outdated")
|
||||
|
||||
|
||||
def upgrade_available(pkg, refresh=True):
|
||||
'''
|
||||
Check whether or not an upgrade is available for a given package
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.upgrade_available <package name>
|
||||
'''
|
||||
return pkg in list_upgrades(refresh=refresh)
|
||||
|
||||
|
||||
def refresh_db():
|
||||
'''
|
||||
Update ports with ``port selfupdate``
|
||||
'''
|
||||
__salt__['cmd.run_all']("port selfupdate")
|
||||
|
||||
|
||||
def upgrade(refresh=True):
|
||||
'''
|
||||
Run a full upgrade
|
||||
|
||||
Options:
|
||||
|
||||
refresh
|
||||
Update ports with ``port selfupdate``
|
||||
|
||||
Return a dict containing the new package names and versions::
|
||||
|
||||
{'<package>': {'old': '<old-version>',
|
||||
'new': '<new-version>'}}
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.upgrade
|
||||
'''
|
||||
|
||||
old = list_pkgs()
|
||||
|
||||
for pkg in list_upgrades(refresh=refresh):
|
||||
__salt__["pkg.install"](pkg)
|
||||
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
return __salt__['pkg_resource.find_changes'](old, new)
|
@ -222,7 +222,7 @@ def volume_list(search_opts=None, profile=None):
|
||||
return volume
|
||||
|
||||
|
||||
def volume_show(volume_name, profile=None):
|
||||
def volume_show(name, profile=None):
|
||||
'''
|
||||
Create a block storage volume
|
||||
|
||||
@ -241,7 +241,7 @@ def volume_show(volume_name, profile=None):
|
||||
'''
|
||||
nt_ks = _auth(profile, service_type='volume')
|
||||
volumes = volume_list(
|
||||
search_opts={'display_name': volume_name},
|
||||
search_opts={'display_name': name},
|
||||
profile=profile
|
||||
)
|
||||
try:
|
||||
@ -321,37 +321,14 @@ def volume_delete(name, profile=None):
|
||||
return response
|
||||
|
||||
|
||||
def volume_delete(volume_name, profile=None):
|
||||
'''
|
||||
Create a block storage volume
|
||||
|
||||
name
|
||||
Name of the new volume (must be first)
|
||||
|
||||
profile
|
||||
Profile to build on
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' nova.delete myblock profile=openstack
|
||||
|
||||
'''
|
||||
nt_ks = _auth(profile, service_type='volume')
|
||||
volume = volume_show(volume_name, profile)
|
||||
response = nt_ks.volumes.delete(volume['id'])
|
||||
return response
|
||||
|
||||
|
||||
def volume_detach(volume_name,
|
||||
def volume_detach(name,
|
||||
server_name,
|
||||
profile=None,
|
||||
timeout=300):
|
||||
'''
|
||||
Attach a block storage volume
|
||||
|
||||
volume_name
|
||||
name
|
||||
Name of the new volume to attach
|
||||
|
||||
server_name
|
||||
@ -368,7 +345,7 @@ def volume_detach(volume_name,
|
||||
|
||||
'''
|
||||
nt_ks = _auth(profile)
|
||||
volume = volume_show(volume_name, profile)
|
||||
volume = volume_show(name, profile)
|
||||
server = server_by_name(server_name, profile)
|
||||
response = nt_ks.volumes.delete_server_volume(
|
||||
server['id'],
|
||||
@ -383,7 +360,7 @@ def volume_detach(volume_name,
|
||||
if response['status'] == 'available':
|
||||
return response
|
||||
except Exception as exc:
|
||||
log.debug('Volume is detaching: {0}'.format(volume_name))
|
||||
log.debug('Volume is detaching: {0}'.format(name))
|
||||
time.sleep(1)
|
||||
if time.time() - start > timeout:
|
||||
log.error('Timed out after {0} seconds '
|
||||
@ -395,7 +372,7 @@ def volume_detach(volume_name,
|
||||
)
|
||||
|
||||
|
||||
def volume_attach(volume_name,
|
||||
def volume_attach(name,
|
||||
server_name,
|
||||
device='/dev/xvdb',
|
||||
profile=None,
|
||||
@ -403,7 +380,7 @@ def volume_attach(volume_name,
|
||||
'''
|
||||
Attach a block storage volume
|
||||
|
||||
volume_name
|
||||
name
|
||||
Name of the new volume to attach
|
||||
|
||||
server_name
|
||||
@ -425,7 +402,7 @@ def volume_attach(volume_name,
|
||||
|
||||
'''
|
||||
nt_ks = _auth(profile)
|
||||
volume = volume_show(volume_name, profile)
|
||||
volume = volume_show(name, profile)
|
||||
server = server_by_name(server_name, profile)
|
||||
response = nt_ks.volumes.create_server_volume(
|
||||
server['id'],
|
||||
@ -441,7 +418,7 @@ def volume_attach(volume_name,
|
||||
if response['status'] == 'in-use':
|
||||
return response
|
||||
except Exception as exc:
|
||||
log.debug('Volume is attaching: {0}'.format(volume_name))
|
||||
log.debug('Volume is attaching: {0}'.format(name))
|
||||
time.sleep(1)
|
||||
if time.time() - start > timeout:
|
||||
log.error('Timed out after {0} seconds '
|
||||
|
@ -75,14 +75,11 @@ def read_key(hkey, path, key):
|
||||
|
||||
registry = Registry()
|
||||
hkey2 = getattr(registry, hkey)
|
||||
# handle = _winreg.OpenKey(hkey2, path)
|
||||
# value, type = _winreg.QueryValueEx(handle, key)
|
||||
# return value
|
||||
try:
|
||||
handle = _winreg.OpenKey(hkey2, path)
|
||||
return _winreg.QueryValueEx(handle, key)[0]
|
||||
except Exception:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def set_key(hkey, path, key, value, vtype='REG_DWORD'):
|
||||
|
@ -2,8 +2,15 @@
|
||||
'''
|
||||
Connection module for Amazon S3
|
||||
|
||||
:configuration: This module is not usable until the following are specified
|
||||
either in a pillar or in the minion's config file::
|
||||
:configuration: This module accepts explicit s3 credentials but can also utilize
|
||||
IAM roles assigned to the instance trough Instance Profiles. Dynamic
|
||||
credentials are then automatically obtained from AWS API and no further
|
||||
configuration is necessary. More Information available at::
|
||||
|
||||
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
|
||||
|
||||
If IAM roles are not used you need to specify them either in a pillar or
|
||||
in the minion's config file::
|
||||
|
||||
s3.keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
s3.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
|
118
salt/modules/znc.py
Normal file
118
salt/modules/znc.py
Normal file
@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
znc - An advanced IRC bouncer
|
||||
|
||||
.. versionadded:: Helium
|
||||
|
||||
Provides an interace to basic ZNC functionality
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
import hashlib
|
||||
import logging
|
||||
import os.path
|
||||
import random
|
||||
import signal
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load the module if znc is installed
|
||||
'''
|
||||
if salt.utils.which('znc'):
|
||||
return 'znc'
|
||||
return False
|
||||
|
||||
|
||||
def _makepass(password, hasher='sha256'):
|
||||
'''
|
||||
Create a znc compatible hashed password
|
||||
'''
|
||||
# Setup the hasher
|
||||
if hasher == 'sha256':
|
||||
h = hashlib.sha256(password)
|
||||
elif hasher == 'md5':
|
||||
h = hashlib.md5(password)
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
c = "abcdefghijklmnopqrstuvwxyz" \
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
|
||||
"0123456789!?.,:;/*-+_()"
|
||||
r = {
|
||||
'Method': h.name,
|
||||
'Salt': ''.join(random.choice(c) for x in xrange(20)),
|
||||
}
|
||||
|
||||
# Salt the password hash
|
||||
h.update(r['Salt'])
|
||||
r['Hash'] = h.hexdigest()
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def buildmod(*modules):
|
||||
'''
|
||||
Build module using znc-buildmod
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' znc.buildmod module.cpp [...]
|
||||
'''
|
||||
# Check if module files are missing
|
||||
missing = [module for module in modules if not os.path.exists(module)]
|
||||
if missing:
|
||||
return 'Error: The file ({0}) does not exist.'.format(', '.join(missing))
|
||||
|
||||
cmd = 'znc-buildmod {0}'.format(' '.join(modules))
|
||||
out = __salt__['cmd.run'](cmd).splitlines()
|
||||
return out[-1]
|
||||
|
||||
|
||||
def dumpconf():
|
||||
'''
|
||||
Wite the active configuration state to config file
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' znc.dumpconf
|
||||
'''
|
||||
return __salt__['ps.pkill']('znc', signal=signal.SIGUSR1)
|
||||
|
||||
|
||||
def rehashconf():
|
||||
'''
|
||||
Rehash the active configuration state from config file
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' znc.rehashconf
|
||||
'''
|
||||
return __salt__['ps.pkill']('znc', signal=signal.SIGHUP)
|
||||
|
||||
|
||||
def version():
|
||||
'''
|
||||
Return server version from znc --version
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' znc.version
|
||||
'''
|
||||
cmd = 'znc --version'
|
||||
out = __salt__['cmd.run'](cmd).splitlines()
|
||||
ret = out[0].split(' - ')
|
||||
return ret[0]
|
@ -168,7 +168,6 @@ TODO
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from salt.loader import states
|
||||
from salt.utils.pyobjects import StateRegistry, StateFactory, SaltObject
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -183,6 +182,12 @@ def render(template, saltenv='base', sls='',
|
||||
|
||||
_registry = StateRegistry()
|
||||
if _states is None:
|
||||
try:
|
||||
_states = __states__
|
||||
except NameError:
|
||||
from salt.loader import states
|
||||
__opts__['grains'] = __grains__
|
||||
__opts__['pillar'] = __pillar__
|
||||
_states = states(__opts__, __salt__)
|
||||
|
||||
# build our list of states and functions
|
||||
|
57
salt/runners/map.py
Normal file
57
salt/runners/map.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
|
||||
General map/reduce style salt-runner for aggregating identical results returned by several different minions.
|
||||
Aggregated results are sorted by the size of the minion pools returning identical results.
|
||||
|
||||
Useful for playing the game: " some of these things are not like the others... " when identifying discrepancies
|
||||
in a large infrastructure managed by salt.
|
||||
|
||||
'''
|
||||
|
||||
import salt.client
|
||||
|
||||
|
||||
def hash(*args, **kwargs):
|
||||
'''
|
||||
Return the aggregated and sorted results from a salt command submitted by a
|
||||
salt runner...
|
||||
|
||||
CLI Example #1: ( functionally equivalent to "salt-run manage.up" )
|
||||
|
||||
salt-run map.hash "*" test.ping
|
||||
|
||||
CLI Example #2: ( find an "outlier" minion config file )
|
||||
|
||||
salt-run map.hash "*" file.get_hash /etc/salt/minion
|
||||
|
||||
'''
|
||||
import hashlib
|
||||
|
||||
tgt = args[0]
|
||||
cmd = args[1]
|
||||
|
||||
client = salt.client.LocalClient(__opts__['conf_file'])
|
||||
minions = client.cmd(tgt, cmd, args[2:], timeout=__opts__['timeout'])
|
||||
ret = {}
|
||||
# hash minion return values as a string
|
||||
for minion in minions:
|
||||
h = hashlib.sha256(str(minions[minion])).hexdigest()
|
||||
if not h in ret:
|
||||
ret[h] = []
|
||||
|
||||
ret[h].append(minion)
|
||||
|
||||
for k in sorted(ret, key=lambda k: len(ret[k]), reverse=True):
|
||||
# return aggregated results, sorted by size of the hash pool
|
||||
# TODO: use a custom outputter for better display results
|
||||
|
||||
print 'minion pool:\n--------'
|
||||
print ret[k]
|
||||
print 'size:\n-----'
|
||||
print ' ' + str(len(ret[k]))
|
||||
print 'result:\n-------'
|
||||
print ' ' + str(minions[ret[k][0]])
|
||||
print '\n'
|
||||
|
||||
return ret
|
@ -596,7 +596,7 @@ class State(object):
|
||||
)
|
||||
self.functions[f_key] = funcs[func]
|
||||
self.states = salt.loader.states(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions, states=self.states)
|
||||
|
||||
def module_refresh(self):
|
||||
'''
|
||||
@ -2615,7 +2615,7 @@ class MasterState(State):
|
||||
# Load the states, but they should not be used in this class apart
|
||||
# from inspection
|
||||
self.states = salt.loader.states(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions)
|
||||
self.rend = salt.loader.render(self.opts, self.functions, states=self.states)
|
||||
|
||||
|
||||
class MasterHighState(HighState):
|
||||
|
@ -24,7 +24,7 @@ parameters used by Salt to define the various timing values for a cron job:
|
||||
the cron job is for another user, it is necessary to specify that user with
|
||||
the ``user`` parameter.
|
||||
|
||||
In a time, a long ago when making changes to an existing cron job,
|
||||
A long time ago (before 2014.2), when making changes to an existing cron job,
|
||||
the name declaration is the parameter used to uniquely identify the job,
|
||||
so if an existing cron that looks like this:
|
||||
|
||||
@ -48,8 +48,9 @@ Is changed to this:
|
||||
Then the existing cron will be updated, but if the cron command is changed,
|
||||
then a new cron job will be added to the user's crontab.
|
||||
|
||||
The current behavior is still relying on that mecanism, but you can also
|
||||
The current behavior is still relying on that mechanism, but you can also
|
||||
specify an identifier to identify your crontabs:
|
||||
.. versionadded:: 2014.2
|
||||
.. code-block:: yaml
|
||||
|
||||
date > /tmp/crontest:
|
||||
@ -59,7 +60,8 @@ specify an identifier to identify your crontabs:
|
||||
- minute: 7
|
||||
- hour: 2
|
||||
|
||||
And, some monthes later, you modify it:
|
||||
And, some months later, you modify it:
|
||||
.. versionadded:: 2014.2
|
||||
.. code-block:: yaml
|
||||
|
||||
superscript > /tmp/crontest:
|
||||
@ -72,7 +74,7 @@ And, some monthes later, you modify it:
|
||||
The old **date > /tmp/crontest** will be replaced by
|
||||
**superscript > /tmp/crontest**.
|
||||
|
||||
Additionaly, Salt also supports running a cron every ``x minutes`` very similarly to the Unix
|
||||
Additionally, Salt also supports running a cron every ``x minutes`` very similarly to the Unix
|
||||
convention of using ``*/5`` to have a job run every five minutes. In Salt, this
|
||||
looks like:
|
||||
|
||||
@ -241,8 +243,8 @@ def present(name,
|
||||
User comment to be added on line previous the cron job
|
||||
|
||||
identifier
|
||||
Custom defined identifier for tracking the cron line for futur crontab
|
||||
edits. This defaults to state id
|
||||
Custom-defined identifier for tracking the cron line for future crontab
|
||||
edits. This defaults to the state id
|
||||
'''
|
||||
name = ' '.join(name.strip().split())
|
||||
if not identifier:
|
||||
@ -315,8 +317,8 @@ def absent(name,
|
||||
the root user
|
||||
|
||||
identifier
|
||||
Custom defined identifier for tracking the cron line for futur crontab
|
||||
edits. This defaults to state id
|
||||
Custom-defined identifier for tracking the cron line for future crontab
|
||||
edits. This defaults to the state id
|
||||
'''
|
||||
### NOTE: The keyword arguments in **kwargs are ignored in this state, but
|
||||
### cannot be removed from the function definition, otherwise the use
|
||||
|
@ -10,10 +10,6 @@ import os
|
||||
# Import salt libs
|
||||
from salt._compat import string_types
|
||||
|
||||
__func_alias__ = {
|
||||
'set_': 'set'
|
||||
}
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
@ -22,19 +18,43 @@ def __virtual__():
|
||||
return True
|
||||
|
||||
|
||||
def set_(name, value):
|
||||
def setenv(name,
|
||||
value,
|
||||
false_unsets=False,
|
||||
clear_all=False,
|
||||
update_minion=False):
|
||||
'''
|
||||
Set the salt process environment variables.
|
||||
|
||||
name
|
||||
The environment variable key to set if 'value' is a string
|
||||
The environment key to set. Must be a string.
|
||||
|
||||
value
|
||||
Either a string or dict. When string, it will be the value
|
||||
set for the environment variable of 'name' above.
|
||||
set for the environment key of 'name' above.
|
||||
When a dict, each key/value pair represents an environment
|
||||
variable to set.
|
||||
|
||||
false_unsets
|
||||
If a key's value is False and false_unsets is True, then the
|
||||
key will be removed from the salt processes environment dict
|
||||
entirely. If a key's value is Flase and false_unsets is not
|
||||
True, then the key's value will be set to an empty string.
|
||||
Default: False
|
||||
|
||||
clear_all
|
||||
USE WITH CAUTION! This option can unset environment variables
|
||||
needed for salt to function properly.
|
||||
If clear_all is True, then any environment variables not
|
||||
defined in the environ dict will be deleted.
|
||||
Default: False
|
||||
|
||||
update_minion
|
||||
If True, apply these environ changes to the main salt-minion
|
||||
process. If False, the environ changes will only affect the
|
||||
current salt subprocess.
|
||||
Default: False
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
@ -43,6 +63,7 @@ def set_(name, value):
|
||||
environ.set:
|
||||
- name: foo
|
||||
- value: bar
|
||||
- update_minion: True
|
||||
|
||||
a_dict_env:
|
||||
environ.set:
|
||||
@ -65,12 +86,42 @@ def set_(name, value):
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Environ value must be string or dict'
|
||||
return ret
|
||||
|
||||
if clear_all is True:
|
||||
# Any keys not in 'environ' dict supplied by user will be unset
|
||||
to_unset = [key for key in os.environ.keys() if key not in environ]
|
||||
for key in to_unset:
|
||||
if false_unsets is not True:
|
||||
# This key value will change to ''
|
||||
ret['changes'].update({key: ''})
|
||||
else:
|
||||
# We're going to delete the key
|
||||
ret['changes'].update({key: None})
|
||||
|
||||
current_environ = dict(os.environ)
|
||||
already_set = []
|
||||
for key, val in environ.items():
|
||||
if current_environ.get(key, '') == val:
|
||||
if val is False:
|
||||
# We unset this key from the environment if
|
||||
# false_unsets is True. Otherwise we want to set
|
||||
# the value to ''
|
||||
if current_environ.get(key, None) is None:
|
||||
# The key does not exist in environment
|
||||
if false_unsets is not True:
|
||||
# This key will be added with value ''
|
||||
ret['changes'].update({key: ''})
|
||||
else:
|
||||
# The key exists.
|
||||
if false_unsets is not True:
|
||||
# Check to see if the value will change
|
||||
if current_environ.get(key, None) != '':
|
||||
# This key value will change to ''
|
||||
ret['changes'].update({key: ''})
|
||||
else:
|
||||
# We're going to delete the key
|
||||
ret['changes'].update({key: None})
|
||||
elif current_environ.get(key, '') == val:
|
||||
already_set.append(key)
|
||||
environ.pop(key)
|
||||
else:
|
||||
ret['changes'].update({key: val})
|
||||
|
||||
@ -82,7 +133,10 @@ def set_(name, value):
|
||||
ret['comment'] = 'Environ values are already set with the correct values'
|
||||
return ret
|
||||
|
||||
environ_ret = __salt__['environ.set'](environ)
|
||||
environ_ret = __salt__['environ.setenv'](environ,
|
||||
false_unsets,
|
||||
clear_all,
|
||||
update_minion)
|
||||
if not environ_ret:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to set environ variables'
|
||||
|
@ -47,7 +47,7 @@ def volume_exists(name, profile=None, **kwargs):
|
||||
if not ret['result']:
|
||||
return ret
|
||||
|
||||
volume = __salt__['nova.volume_show'](volume_name=name, profile=profile)
|
||||
volume = __salt__['nova.volume_show'](name=name, profile=profile)
|
||||
|
||||
if volume:
|
||||
ret['comment'] = 'Volume exists: {0}'.format(name)
|
||||
@ -58,17 +58,16 @@ def volume_exists(name, profile=None, **kwargs):
|
||||
ret['result'] = None
|
||||
return ret
|
||||
|
||||
try:
|
||||
response = __salt__['nova.volume_create'](
|
||||
name=name,
|
||||
profile=profile,
|
||||
**kwargs
|
||||
)
|
||||
if response:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Volume {0} was created'.format(name)
|
||||
ret['changes'] = {'old': None, 'new': response}
|
||||
return ret
|
||||
except:
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Volume {0} failed to create.'.format(name)
|
||||
return ret
|
||||
@ -87,13 +86,12 @@ def volume_attached(name, server_name, profile=None, **kwargs):
|
||||
return ret
|
||||
|
||||
volume = __salt__['nova.volume_show'](
|
||||
volume_name=name,
|
||||
name=name,
|
||||
profile=profile
|
||||
)
|
||||
server = __salt__['nova.server_by_name'](server_name, profile=profile)
|
||||
|
||||
if volume and volume['attachments']:
|
||||
print(volume)
|
||||
ret['comment'] = ('Volume {name} is already'
|
||||
'attached: {attachments}').format(**volume)
|
||||
ret['result'] = True
|
||||
@ -113,18 +111,17 @@ def volume_attached(name, server_name, profile=None, **kwargs):
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
try:
|
||||
response = __salt__['nova.volume_attach'](
|
||||
name,
|
||||
server_name,
|
||||
profile=profile,
|
||||
**kwargs
|
||||
)
|
||||
if response:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Volume {0} was created'.format(name)
|
||||
ret['changes'] = {'old': volume, 'new': response}
|
||||
return ret
|
||||
except:
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Volume {0} failed to attach.'.format(name)
|
||||
return ret
|
||||
|
@ -29,6 +29,7 @@ import logging
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt._compat
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -54,7 +55,7 @@ def state(
|
||||
sls=None,
|
||||
env=None,
|
||||
test=False,
|
||||
fail_minions='',
|
||||
fail_minions=None,
|
||||
allow_fail=0,
|
||||
timeout=None):
|
||||
'''
|
||||
@ -93,7 +94,7 @@ def state(
|
||||
fail_minions
|
||||
An optional list of targeted minions where failure is an option
|
||||
'''
|
||||
cmd_kw = {'arg': [], 'ret': ret, 'timeout': timeout}
|
||||
cmd_kw = {'arg': [], 'kwarg': {}, 'ret': ret, 'timeout': timeout}
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
@ -135,10 +136,12 @@ def state(
|
||||
ret['comment'] = 'No highstate or sls specified, no execution made'
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
if test:
|
||||
cmd_kw['arg'].append('test={0}'.format(test))
|
||||
if __env__ != 'base':
|
||||
cmd_kw['arg'].append('saltenv={0}'.format(__env__))
|
||||
cmd_kw['kwarg']['test'] = test
|
||||
|
||||
cmd_kw['kwarg']['saltenv'] = __env__
|
||||
|
||||
if __opts__['test'] is True:
|
||||
ret['comment'] = (
|
||||
'State run to be executed on target {0} as test={1}'
|
||||
@ -151,8 +154,17 @@ def state(
|
||||
fail = set()
|
||||
failures = {}
|
||||
no_change = set()
|
||||
if isinstance(fail_minions, str):
|
||||
fail_minions = [fail_minions]
|
||||
|
||||
if fail_minions is None:
|
||||
fail_minions = ()
|
||||
elif isinstance(fail_minions, salt._compat.string_types):
|
||||
fail_minions = [minion.strip() for minion in fail_minions.split(',')]
|
||||
elif not isinstance(fail_minions, list):
|
||||
ret.setdefault('warnings', []).append(
|
||||
'\'fail_minions\' needs to be a list or a comma separated '
|
||||
'string. Ignored.'
|
||||
)
|
||||
fail_minions = ()
|
||||
|
||||
for minion, mdata in cmd_ret.iteritems():
|
||||
if mdata['out'] != 'highstate':
|
||||
|
@ -3,6 +3,7 @@
|
||||
Tests to try out packeting. Potentially ephemeral
|
||||
|
||||
'''
|
||||
# pylint: skip-file
|
||||
# pylint: disable=C0103
|
||||
|
||||
from ioflo.base.odicting import odict
|
||||
|
@ -427,6 +427,43 @@ def wait_for_port(host, port=22, timeout=900, gateway=None):
|
||||
)
|
||||
|
||||
|
||||
def wait_for_winexesvc(host, port, username, password, timeout=900, gateway=None):
|
||||
'''
|
||||
Wait until winexe connection can be established.
|
||||
'''
|
||||
start = time.time()
|
||||
log.debug(
|
||||
'Attempting winexe connection to host {0} on port {1}'.format(
|
||||
host, port
|
||||
)
|
||||
)
|
||||
creds = '-U {0}%{1} //{2}'.format(
|
||||
username, password, host)
|
||||
trycount = 0
|
||||
while True:
|
||||
trycount += 1
|
||||
try:
|
||||
# Shell out to winexe to check %TEMP%
|
||||
ret_code = win_cmd('winexe {0} "sc query winexesvc"'.format(creds))
|
||||
if ret_code == 0:
|
||||
log.debug('winexe connected...')
|
||||
return True
|
||||
log.debug('Return code was {0}'.format(ret_code))
|
||||
time.sleep(1)
|
||||
except socket.error as exc:
|
||||
log.debug('Caught exception in wait_for_winexesvc: {0}'.format(exc))
|
||||
time.sleep(1)
|
||||
if time.time() - start > timeout:
|
||||
log.error('winexe connection timed out: {0}'.format(timeout))
|
||||
return False
|
||||
log.debug(
|
||||
'Retrying winexe connection to host {0} on port {1} '
|
||||
'(try {2})'.format(
|
||||
host, port, trycount
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def validate_windows_cred(host, username='Administrator', password=None):
|
||||
'''
|
||||
Check if the windows credentials are valid
|
||||
@ -512,7 +549,10 @@ def deploy_windows(host, port=445, timeout=900, username='Administrator',
|
||||
'''
|
||||
starttime = time.mktime(time.localtime())
|
||||
log.debug('Deploying {0} at {1} (Windows)'.format(host, starttime))
|
||||
if wait_for_port(host=host, port=port, timeout=port_timeout * 60):
|
||||
if wait_for_port(host=host, port=port, timeout=port_timeout * 60) and \
|
||||
wait_for_winexesvc(host=host, port=port,
|
||||
username=username, password=password,
|
||||
timeout=port_timeout * 60):
|
||||
log.debug('SMB port {0} on {1} is available'.format(port, host))
|
||||
newtimeout = timeout - (time.mktime(time.localtime()) - starttime)
|
||||
log.debug(
|
||||
|
@ -2149,17 +2149,14 @@ class SaltSSHOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
|
||||
help=('Don\'t execute a salt routine on the targets, execute a '
|
||||
'raw shell command')
|
||||
)
|
||||
self.add_option(
|
||||
'--priv',
|
||||
dest='ssh_priv',
|
||||
help=('Ssh private key file'))
|
||||
self.add_option(
|
||||
'--roster',
|
||||
dest='roster',
|
||||
default='',
|
||||
help=('Define which roster system to use, this defines if a '
|
||||
'database backend, scanner, or custom roster system is '
|
||||
'used. Default is the flat file roster.'))
|
||||
'used. Default is the flat file roster.')
|
||||
)
|
||||
self.add_option(
|
||||
'--roster-file',
|
||||
dest='roster_file',
|
||||
@ -2167,7 +2164,8 @@ class SaltSSHOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
|
||||
help=('define an alternative location for the default roster '
|
||||
'file location. The default roster file is called roster '
|
||||
'and is found in the same directory as the master config '
|
||||
'file.'))
|
||||
'file.')
|
||||
)
|
||||
self.add_option(
|
||||
'--refresh', '--refresh-cache',
|
||||
dest='refresh_cache',
|
||||
@ -2176,7 +2174,15 @@ class SaltSSHOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
|
||||
help=('Force a refresh of the master side data cache of the '
|
||||
'target\'s data. This is needed if a target\'s grains have '
|
||||
'been changed and the auto refresh timeframe has not been '
|
||||
'reached.'))
|
||||
'reached.')
|
||||
)
|
||||
self.add_option(
|
||||
'-v', '--verbose',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=('Turn on command verbosity, display jid')
|
||||
)
|
||||
|
||||
self.add_option(
|
||||
'--max-procs',
|
||||
dest='ssh_max_procs',
|
||||
@ -2185,35 +2191,50 @@ class SaltSSHOptionParser(OptionParser, ConfigDirMixIn, MergeConfigMixIn,
|
||||
help='Set the number of concurrent minions to communicate with. '
|
||||
'This value defines how many processes are opened up at a '
|
||||
'time to manage connections, the more running processes the '
|
||||
'faster communication should be, default is %default')
|
||||
self.add_option(
|
||||
'-i',
|
||||
'--ignore-host-keys',
|
||||
dest='ignore_host_keys',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='By default ssh host keys are honored and connections will '
|
||||
'ask for approval')
|
||||
'faster communication should be, default is %default'
|
||||
)
|
||||
self.add_option(
|
||||
'-v', '--verbose',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=('Turn on command verbosity, display jid')
|
||||
)
|
||||
self.add_option(
|
||||
|
||||
auth_group = optparse.OptionGroup(
|
||||
self, 'Authentication Options',
|
||||
'Parameters affecting authentication'
|
||||
)
|
||||
auth_group.add_option(
|
||||
'--priv',
|
||||
dest='ssh_priv',
|
||||
help='Ssh private key file'
|
||||
)
|
||||
auth_group.add_option(
|
||||
'-i',
|
||||
'--ignore-host-keys',
|
||||
dest='ignore_host_keys',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='By default ssh host keys are honored and connections will '
|
||||
'ask for approval'
|
||||
)
|
||||
auth_group.add_option(
|
||||
'--passwd',
|
||||
dest='ssh_passwd',
|
||||
default='',
|
||||
help='Set the default password to attempt to use when '
|
||||
'authenticating')
|
||||
self.add_option(
|
||||
'authenticating'
|
||||
)
|
||||
auth_group.add_option(
|
||||
'--key-deploy',
|
||||
dest='ssh_key_deploy',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Set this flag to atempt to deploy the authorized ssh key '
|
||||
'with all minions. This combined with --passwd can make '
|
||||
'initial deployment of keys very fast and easy')
|
||||
'initial deployment of keys very fast and easy'
|
||||
)
|
||||
self.add_option_group(auth_group)
|
||||
|
||||
def _mixin_after_parsed(self):
|
||||
if not self.args:
|
||||
|
@ -39,7 +39,7 @@ class MacUserTestCase(TestCase):
|
||||
mock_getgrall = [grp.struct_group(('accessibility', '*', 90, [])),
|
||||
grp.struct_group(('admin', '*', 80, ['root', 'admin']))]
|
||||
mock_info_ret = {'shell': '/bin/bash', 'name': 'test', 'gid': 4376,
|
||||
'groups': ['TEST_GROUP'], 'home': '/var/test',
|
||||
'groups': ['TEST_GROUP'], 'home': '/Users/foo',
|
||||
'fullname': 'TEST USER', 'uid': 4376}
|
||||
|
||||
def test_osmajor(self):
|
||||
@ -110,16 +110,14 @@ class MacUserTestCase(TestCase):
|
||||
'''
|
||||
Tests if the uid is an int
|
||||
'''
|
||||
with self.assertRaises(SaltInvocationError):
|
||||
mac_user.chuid('foo', 'foo')
|
||||
self.assertRaises(SaltInvocationError, mac_user.chuid, 'foo', 'foo')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chuid_user_exists(self):
|
||||
'''
|
||||
Tests if the user exists or not
|
||||
'''
|
||||
with self.assertRaises(CommandExecutionError):
|
||||
mac_user.chuid('foo', 4376)
|
||||
self.assertRaises(CommandExecutionError, mac_user.chuid, 'foo', 4376)
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chuid_same_uid(self):
|
||||
@ -132,16 +130,14 @@ class MacUserTestCase(TestCase):
|
||||
'''
|
||||
Tests if the gid is an int
|
||||
'''
|
||||
with self.assertRaises(SaltInvocationError):
|
||||
mac_user.chgid('foo', 'foo')
|
||||
self.assertRaises(SaltInvocationError, mac_user.chgid, 'foo', 'foo')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chgid_user_exists(self):
|
||||
'''
|
||||
Tests if the user exists or not
|
||||
'''
|
||||
with self.assertRaises(CommandExecutionError):
|
||||
mac_user.chgid('foo', 4376)
|
||||
self.assertRaises(CommandExecutionError, mac_user.chgid, 'foo', 4376)
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chgid_same_gid(self):
|
||||
@ -150,6 +146,62 @@ class MacUserTestCase(TestCase):
|
||||
'''
|
||||
self.assertTrue(mac_user.chgid('foo', 4376))
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chshell_user_exists(self):
|
||||
'''
|
||||
Tests if the user exists or not
|
||||
'''
|
||||
self.assertRaises(CommandExecutionError, mac_user.chshell, 'foo', '/bin/bash')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chshell_same_shell(self):
|
||||
'''
|
||||
Tests if the user's shell is the same as the argument
|
||||
'''
|
||||
self.assertTrue(mac_user.chshell('foo', '/bin/bash'))
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chhome_user_exists(self):
|
||||
'''
|
||||
Test if the user exists or not
|
||||
'''
|
||||
self.assertRaises(CommandExecutionError, mac_user.chhome, 'foo', '/Users/foo')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chhome_same_home(self):
|
||||
'''
|
||||
Tests if the user's home is the same as the argument
|
||||
'''
|
||||
self.assertTrue(mac_user.chhome('foo', '/Users/foo'))
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chfullname_user_exists(self):
|
||||
'''
|
||||
Tests if the user exists or not
|
||||
'''
|
||||
self.assertRaises(CommandExecutionError, mac_user.chfullname, 'test', 'TEST USER')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chfullname_same_name(self):
|
||||
'''
|
||||
Tests if the user's full name is the same as the argument
|
||||
'''
|
||||
self.assertTrue(mac_user.chfullname('test', 'TEST USER'))
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value={}))
|
||||
def test_chgroups_user_exists(self):
|
||||
'''
|
||||
Tests if the user exists or not
|
||||
'''
|
||||
self.assertRaises(CommandExecutionError, mac_user.chgroups, 'foo', 'wheel, root')
|
||||
|
||||
@patch('salt.modules.mac_user.info', MagicMock(return_value=mock_info_ret))
|
||||
def test_chgroups_bad_groups(self):
|
||||
'''
|
||||
Test if there is white space in groups argument
|
||||
'''
|
||||
self.assertRaises(SaltInvocationError, mac_user.chgroups, 'test', 'bad group')
|
||||
|
||||
@patch('salt.modules.mac_user.list_groups',
|
||||
MagicMock(return_value=['_TEST_GROUP']))
|
||||
def test_info(self):
|
||||
|
Loading…
Reference in New Issue
Block a user