mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #27111 from basepi/merge-forward-2015.8
[2015.8] Merge forward from 2015.5 to 2015.8
This commit is contained in:
commit
0d62d3470c
49 changed files with 1017 additions and 626 deletions
|
@ -570,7 +570,7 @@
|
|||
# master config file that can then be used on minions.
|
||||
#pillar_opts: False
|
||||
|
||||
# The pillar_safe_render_error option prevents the master from passing piller
|
||||
# The pillar_safe_render_error option prevents the master from passing pillar
|
||||
# render errors to the minion. This is set on by default because the error could
|
||||
# contain templating data which would give that minion information it shouldn't
|
||||
# have, like a password! When set true the error message will only show:
|
||||
|
|
|
@ -46,7 +46,7 @@ Salt's Loader Interface
|
|||
Modules in the Salt ecosystem are loaded into memory using a custom loader
|
||||
system. This allows modules to have conditional requirements (OS, OS version,
|
||||
installed libraries, etc) and allows Salt to inject special variables
|
||||
(``__salt__``, ``__opts``, etc).
|
||||
(``__salt__``, ``__opts__``, etc).
|
||||
|
||||
Most modules can be manually loaded. This is often useful in third-party Python
|
||||
apps or when writing tests. However some modules require and expect a full,
|
||||
|
|
|
@ -340,6 +340,7 @@ You can call the logger from custom modules to write messages to the minion
|
|||
logs. The following code snippet demonstrates writing log messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -235,6 +235,7 @@ You can call the logger from custom modules to write messages to the minion
|
|||
logs. The following code snippet demonstrates writing log messages:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
|
@ -4,20 +4,35 @@
|
|||
Beacons
|
||||
=======
|
||||
|
||||
The beacon system allows the minion to hook into system processes and
|
||||
continually translate external events into the salt event bus. The
|
||||
primary example of this is the :py:mod:`~salt.beacons.inotify` beacon. This
|
||||
beacon uses inotify to watch configured files or directories on the minion for
|
||||
changes, creation, deletion etc.
|
||||
The beacon system allows the minion to hook into a variety of system processes
|
||||
and continually monitor these processes. When monitored activity occurs in
|
||||
a system process, an event is sent on the Salt event bus that can
|
||||
be used to trigger a :ref:`reactor <reactor>`.
|
||||
|
||||
This allows for the changes to be sent up to the master where the
|
||||
reactor can respond to changes.
|
||||
Salt beacons can currently monitor and send Salt events for many system
|
||||
activities, including:
|
||||
|
||||
Configuring The Beacons
|
||||
=======================
|
||||
- file system changes
|
||||
- system load
|
||||
- service status
|
||||
- shell activity, such as user login
|
||||
- network and disk usage
|
||||
|
||||
The beacon system, like many others in Salt, can be configured via the
|
||||
minion pillar, grains, or local config file:
|
||||
See :ref:`beacon modules <all-salt.beacons>` for a current list.
|
||||
|
||||
.. note::
|
||||
Salt beacons are an event generation mechanism. Beacons leverage the Salt
|
||||
:ref:`reactor <reactor>` system to make changes when beacon events occur.
|
||||
|
||||
|
||||
Configuring Beacons
|
||||
===================
|
||||
|
||||
Salt beacons do not require any changes to the system process that
|
||||
is being monitored, everything is configured using Salt.
|
||||
|
||||
Beacons are typically enabled by placing a ``beacons:`` top level block in the
|
||||
minion configuration file:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -26,11 +41,15 @@ minion pillar, grains, or local config file:
|
|||
/etc/httpd/conf.d: {}
|
||||
/opt: {}
|
||||
|
||||
Optionally, a beacon can be run on an interval other than the default
|
||||
``loop_interval``, which is typically set to 1 second.
|
||||
The beacon system, like many others in Salt, can also be configured via the
|
||||
minion pillar, grains, or local config file.
|
||||
|
||||
To run a beacon every 5 seconds, for example, provide an ``interval`` argument
|
||||
to a beacon.
|
||||
Beacon Monitoring Interval
|
||||
--------------------------
|
||||
|
||||
Beacons monitor on a 1-second interval by default. To set a different interval,
|
||||
provide an ``interval`` argument to a beacon. The following beacons run on
|
||||
5- and 10-second intervals:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -51,6 +70,123 @@ to a beacon.
|
|||
- 1.0
|
||||
- interval: 10
|
||||
|
||||
Beacon Example
|
||||
==============
|
||||
|
||||
This example demonstrates configuring the :py:mod:`~salt.beacons.inotify`
|
||||
beacon to monitor a file for changes, and then create a backup each time
|
||||
a change is detected.
|
||||
|
||||
.. note::
|
||||
The inotify beacon requires Pyinotify on the minion, install it using
|
||||
``salt myminion pkg.install python-inotify``.
|
||||
|
||||
First, on the Salt minion, add the following beacon configuration to
|
||||
``/ect/salt/minion``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
beacons:
|
||||
inotify:
|
||||
home/user/importantfile:
|
||||
mask:
|
||||
- modify
|
||||
|
||||
Replace ``user`` in the previous example with the name of your user account,
|
||||
and then save the configuration file and restart the minion service.
|
||||
|
||||
Next, create a file in your home directory named ``importantfile`` and add some
|
||||
simple content. The beacon is now set up to monitor this file for
|
||||
modifications.
|
||||
|
||||
View Events on the Master
|
||||
-------------------------
|
||||
|
||||
On your Salt master, start the event runner using the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run state.event pretty=true
|
||||
|
||||
This runner displays events as they are received on the Salt event bus. To test
|
||||
the beacon you set up in the previous section, make and save
|
||||
a modification to the ``importantfile`` you created. You'll see an event
|
||||
similar to the following on the event bus:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
salt/beacon/minion1/inotify/home/user/importantfile {
|
||||
"_stamp": "2015-09-09T15:59:37.972753",
|
||||
"data": {
|
||||
"change": "IN_IGNORED",
|
||||
"id": "minion1",
|
||||
"path": "/home/user/importantfile"
|
||||
},
|
||||
"tag": "salt/beacon/minion1/inotify/home/user/importantfile"
|
||||
}
|
||||
|
||||
|
||||
This indicates that the event is being captured and sent correctly. Now you can
|
||||
create a reactor to take action when this event occurs.
|
||||
|
||||
Create a Reactor
|
||||
----------------
|
||||
|
||||
On your Salt master, create a file named ``srv/reactor/backup.sls``. If the
|
||||
``reactor`` directory doesn't exist, create it. Add the following to ``backup.sls``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
backup file:
|
||||
cmd.file.copy:
|
||||
- tgt: {{ data['data']['id'] }}
|
||||
- arg:
|
||||
- {{ data['data']['path'] }}
|
||||
- {{ data['data']['path'] }}.bak
|
||||
|
||||
Next, add the code to trigger the reactor to ``ect/salt/master``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
reactor:
|
||||
- salt/beacon/*/inotify/*/importantfile:
|
||||
- /srv/reactor/backup.sls
|
||||
|
||||
|
||||
This reactor creates a backup each time a file named ``importantfile`` is
|
||||
modified on a minion that has the :py:mod:`~salt.beacons.inotify` beacon
|
||||
configured as previously shown.
|
||||
|
||||
.. note::
|
||||
You can have only one top level ``reactor`` section, so if one already
|
||||
exists, add this code to the existing section. See :ref:`Understanding
|
||||
the Structure of Reactor Formulas <reactor-structure>` to learn more about
|
||||
reactor SLS syntax.
|
||||
|
||||
|
||||
Start the Salt Master in Debug Mode
|
||||
-----------------------------------
|
||||
|
||||
To help with troubleshooting, start the Salt master in debug mode:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
service salt-master stop
|
||||
salt-master -l debug
|
||||
|
||||
When debug logging is enabled, event and reactor data are displayed so you can
|
||||
discover syntax and other issues.
|
||||
|
||||
Trigger the Reactor
|
||||
-------------------
|
||||
|
||||
On your minion, make and save another change to ``importantfile``. On the Salt
|
||||
master, you'll see debug messages that indicate the event was received and the
|
||||
``file.copy`` job was sent. When you list the directory on the minion, you'll now
|
||||
see ``importantfile.bak``.
|
||||
|
||||
All beacons are configured using a similar process of enabling the beacon,
|
||||
writing a reactor SLS, and mapping a beacon event to the reactor SLS.
|
||||
|
||||
Writing Beacon Plugins
|
||||
======================
|
||||
|
|
|
@ -11,7 +11,7 @@ available at PyPI:
|
|||
|
||||
https://pypi.python.org/pypi/SoftLayer
|
||||
|
||||
This package can be installed using `pip` or `easy_install`:
|
||||
This package can be installed using ``pip`` or ``easy_install``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -61,13 +61,13 @@ Set up the cloud config at ``/etc/salt/cloud.providers``:
|
|||
|
||||
Access Credentials
|
||||
==================
|
||||
The `user` setting is the same user as is used to log into the SoftLayer
|
||||
Administration area. The `apikey` setting is found inside the Admin area after
|
||||
The ``user`` setting is the same user as is used to log into the SoftLayer
|
||||
Administration area. The ``apikey`` setting is found inside the Admin area after
|
||||
logging in:
|
||||
|
||||
* Hover over the `Administrative` menu item.
|
||||
* Click the `API Access` link.
|
||||
* The `apikey` is located next to the `user` setting.
|
||||
* Hover over the ``Account`` menu item.
|
||||
* Click the ``Users`` link.
|
||||
* Find the ``API Key`` column and click ``View``.
|
||||
|
||||
|
||||
Profiles
|
||||
|
@ -102,13 +102,13 @@ Most of the above items are required; optional items are specified below.
|
|||
|
||||
image
|
||||
-----
|
||||
Images to build an instance can be found using the `--list-images` option:
|
||||
Images to build an instance can be found using the ``--list-images`` option:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt-cloud --list-images my-softlayer
|
||||
|
||||
The setting used will be labeled as `template`.
|
||||
The setting used will be labeled as ``template``.
|
||||
|
||||
cpu_number
|
||||
----------
|
||||
|
|
|
@ -46,7 +46,7 @@ from saltstack.com:
|
|||
|
||||
* `SaltStack Download Area`__
|
||||
|
||||
.. __: http://docs.saltstack.com/downloads/
|
||||
.. __: https://repo.saltstack.com/windows/
|
||||
|
||||
|
||||
Firewall Settings
|
||||
|
|
|
@ -9,7 +9,7 @@ all package development will be done there.
|
|||
Installation
|
||||
============
|
||||
|
||||
Salt can be installed using ``zypper`` and is available in the standard openSUSE 13.1
|
||||
Salt can be installed using ``zypper`` and is available in the standard openSUSE
|
||||
repositories.
|
||||
|
||||
Stable Release
|
||||
|
@ -101,6 +101,16 @@ For openSUSE Factory run the following as root:
|
|||
zypper refresh
|
||||
zypper install salt salt-minion salt-master
|
||||
|
||||
|
||||
For openSUSE 13.2 run the following as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
zypper addrepo http://download.opensuse.org/repositories/devel:languages:python/openSUSE_13.2/devel:languages:python.repo
|
||||
zypper refresh
|
||||
zypper install salt salt-minion salt-master
|
||||
|
||||
|
||||
For openSUSE 13.1 run the following as root:
|
||||
|
||||
.. code-block:: bash
|
||||
|
|
|
@ -32,6 +32,8 @@ The event system fires events with a very specific criteria. Every event has a
|
|||
addition to the tag, each event has a data structure. This data structure is a
|
||||
dict, which contains information about the event.
|
||||
|
||||
.. _reactor-mapping-events:
|
||||
|
||||
Mapping Events to Reactor SLS Files
|
||||
===================================
|
||||
|
||||
|
@ -183,6 +185,8 @@ rendered SLS file (or any errors generated while rendering the SLS file).
|
|||
view the result of referencing Jinja variables. If the result is empty then
|
||||
Jinja produced an empty result and the Reactor will ignore it.
|
||||
|
||||
.. _reactor-structure:
|
||||
|
||||
Understanding the Structure of Reactor Formulas
|
||||
===============================================
|
||||
|
||||
|
@ -273,10 +277,10 @@ Any other parameters in the :py:meth:`LocalClient().cmd()
|
|||
Calling Runner modules and Wheel modules
|
||||
----------------------------------------
|
||||
|
||||
Calling Runner modules and wheel modules from the Reactor uses a more direct
|
||||
Calling Runner modules and Wheel modules from the Reactor uses a more direct
|
||||
syntax since the function is being executed locally instead of sending a
|
||||
command to a remote system to be executed there. There are no 'arg' or 'kwarg'
|
||||
parameters (unless the Runner function or Wheel function accepts a paramter
|
||||
parameters (unless the Runner function or Wheel function accepts a parameter
|
||||
with either of those names.)
|
||||
|
||||
For example:
|
||||
|
|
|
@ -9,15 +9,6 @@ LXC Management with Salt
|
|||
This walkthrough assumes basic knowledge of Salt. To get up to speed, check
|
||||
out the :doc:`Salt Walkthrough </topics/tutorials/walkthrough>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
Some features are only currently available in the ``develop`` branch, and
|
||||
are new in the upcoming 2015.5.0 release. These new features will be
|
||||
clearly labeled.
|
||||
Even in 2015.5 release, you will need up to the last changeset of this
|
||||
stable branch for the salt-cloud stuff to work correctly.
|
||||
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
|
@ -106,7 +97,7 @@ Consider the following container profile data:
|
|||
size: 20G
|
||||
|
||||
Any minion with the above Pillar data would have the **size** parameter in the
|
||||
**centos** profile overriden to 20G, while those minions without the above
|
||||
**centos** profile overridden to 20G, while those minions without the above
|
||||
Pillar data would have the 10G **size** value. This is another way of achieving
|
||||
the same result as the **centos_big** profile above, without having to define
|
||||
another whole profile that differs in just one value.
|
||||
|
@ -155,7 +146,7 @@ get connectivity.
|
|||
|
||||
.. warning::
|
||||
|
||||
on pre **2015.5.2**, you need to specify explitly the network bridge
|
||||
on pre **2015.5.2**, you need to specify explicitly the network bridge
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -249,7 +240,7 @@ container-by-container basis, for instance using the ``nic_opts`` argument to
|
|||
|
||||
|
||||
Old lxc support (<1.0.7)
|
||||
---------------------------
|
||||
------------------------
|
||||
|
||||
With saltstack **2015.5.2** and above, normally the setting is autoselected, but
|
||||
before, you'll need to teach your network profile to set
|
||||
|
@ -265,7 +256,7 @@ Thus you'll need
|
|||
ipv4.gateway: auto
|
||||
|
||||
Tricky network setups Examples
|
||||
-----------------------------------
|
||||
------------------------------
|
||||
This example covers how to make a container with both an internal ip and a
|
||||
public routable ip, wired on two veth pairs.
|
||||
|
||||
|
@ -521,7 +512,7 @@ To run a command and return all information:
|
|||
|
||||
|
||||
Container Management Using salt-cloud
|
||||
========================================
|
||||
=====================================
|
||||
|
||||
Salt cloud uses under the hood the salt runner and module to manage containers,
|
||||
Please look at :ref:`this chapter <config_lxc>`
|
||||
|
|
|
@ -200,7 +200,7 @@ class SaltCMD(parsers.SaltCMDOptionParser):
|
|||
ret_, out, retcode = self._format_ret(full_ret)
|
||||
retcodes.append(retcode)
|
||||
self._output_ret(ret_, out)
|
||||
ret.update(ret_)
|
||||
ret.update(full_ret)
|
||||
except KeyError:
|
||||
errors.append(full_ret)
|
||||
|
||||
|
|
|
@ -348,16 +348,19 @@ class SyncClientMixin(object):
|
|||
data['success'] = False
|
||||
|
||||
namespaced_event.fire_event(data, 'ret')
|
||||
salt.utils.job.store_job(
|
||||
self.opts,
|
||||
{'id': self.opts['id'],
|
||||
'tgt': self.opts['id'],
|
||||
'jid': data['jid'],
|
||||
'return': data,
|
||||
},
|
||||
event=None,
|
||||
mminion=self.mminion,
|
||||
)
|
||||
try:
|
||||
salt.utils.job.store_job(
|
||||
self.opts,
|
||||
{'id': self.opts['id'],
|
||||
'tgt': self.opts['id'],
|
||||
'jid': data['jid'],
|
||||
'return': data,
|
||||
},
|
||||
event=None,
|
||||
mminion=self.mminion,
|
||||
)
|
||||
except salt.exceptions.SaltCacheError:
|
||||
log.error('Could not store job cache info. Job details for this run may be unavailable.')
|
||||
# if we fired an event, make sure to delete the event object.
|
||||
# This will ensure that we call destroy, which will do the 0MQ linger
|
||||
log.info('Runner completed: {0}'.format(data['jid']))
|
||||
|
|
|
@ -1518,6 +1518,7 @@ def _validate_name(name):
|
|||
name
|
||||
The VM name to validate
|
||||
'''
|
||||
name = str(name)
|
||||
name_length = len(name)
|
||||
regex = re.compile(r'^[a-zA-Z0-9][A-Za-z0-9_-]*[a-zA-Z0-9]$')
|
||||
|
||||
|
|
|
@ -157,6 +157,12 @@ class SaltClientTimeout(SaltException):
|
|||
self.jid = jid
|
||||
|
||||
|
||||
class SaltCacheError(SaltException):
|
||||
'''
|
||||
Thrown when a problem was encountered trying to read or write from the salt cache
|
||||
'''
|
||||
|
||||
|
||||
class SaltReqTimeoutError(SaltException):
|
||||
'''
|
||||
Thrown when a salt master request call fails to return within the timeout
|
||||
|
|
|
@ -1198,8 +1198,11 @@ class AESFuncs(object):
|
|||
|
||||
:param dict load: The minion payload
|
||||
'''
|
||||
salt.utils.job.store_job(
|
||||
self.opts, load, event=self.event, mminion=self.mminion)
|
||||
try:
|
||||
salt.utils.job.store_job(
|
||||
self.opts, load, event=self.event, mminion=self.mminion)
|
||||
except salt.exception.SaltCacheError:
|
||||
log.error('Could not store job information for load: {0}'.format(load))
|
||||
|
||||
def _syndic_return(self, load):
|
||||
'''
|
||||
|
|
|
@ -1802,6 +1802,10 @@ def mod_repo(repo, saltenv='base', **kwargs):
|
|||
elif 'key_url' in kwargs:
|
||||
key_url = kwargs['key_url']
|
||||
fn_ = __salt__['cp.cache_file'](key_url, saltenv)
|
||||
if not fn_:
|
||||
raise CommandExecutionError(
|
||||
'Error: file not found: {0}'.format(key_url)
|
||||
)
|
||||
cmd = 'apt-key add {0}'.format(_cmd_quote(fn_))
|
||||
out = __salt__['cmd.run_stdout'](cmd, **kwargs)
|
||||
if not out.upper().startswith('OK'):
|
||||
|
|
|
@ -5,11 +5,15 @@ Manage groups on Linux, OpenBSD and NetBSD
|
|||
|
||||
# Import python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
try:
|
||||
import grp
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'group'
|
||||
|
||||
|
@ -142,12 +146,17 @@ def adduser(name, username):
|
|||
Verifies if a valid username 'bar' as a member of an existing group 'foo',
|
||||
if not then adds it.
|
||||
'''
|
||||
on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5'
|
||||
|
||||
if __grains__['kernel'] == 'Linux':
|
||||
retcode = __salt__['cmd.retcode']('gpasswd --add {0} {1}'.format(
|
||||
username, name), python_shell=False)
|
||||
if on_redhat_5:
|
||||
cmd = 'gpasswd -a {0} {1}'.format(username, name)
|
||||
else:
|
||||
cmd = 'gpasswd --add {0} {1}'.format(username, name)
|
||||
else:
|
||||
retcode = __salt__['cmd.retcode']('usermod -G {0} {1}'.format(
|
||||
name, username), python_shell=False)
|
||||
cmd = 'usermod -G {0} {1}'.format(name, username)
|
||||
|
||||
retcode = __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
|
||||
return not retcode
|
||||
|
||||
|
@ -165,21 +174,27 @@ def deluser(name, username):
|
|||
Removes a member user 'bar' from a group 'foo'. If group is not present
|
||||
then returns True.
|
||||
'''
|
||||
on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5'
|
||||
|
||||
grp_info = __salt__['group.info'](name)
|
||||
try:
|
||||
if username in grp_info['members']:
|
||||
if __grains__['kernel'] == 'Linux':
|
||||
retcode = __salt__['cmd.retcode']('gpasswd --del {0} {1}'
|
||||
.format(username, name), python_shell=False)
|
||||
if on_redhat_5:
|
||||
cmd = 'gpasswd -d {0} {1}'.format(username, name)
|
||||
else:
|
||||
cmd = 'gpasswd --del {0} {1}'.format(username, name)
|
||||
retcode = __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
elif __grains__['kernel'] == 'OpenBSD':
|
||||
cmd = 'usermod -S '
|
||||
out = __salt__['cmd.run_stdout']('id -Gn {0}'.format(username),
|
||||
python_shell=False)
|
||||
for group in out.split(" "):
|
||||
if group != format(name):
|
||||
cmd += '{0},'.format(group)
|
||||
retcode = __salt__['cmd.retcode']('{0} {1}'.format(
|
||||
cmd, username), python_shell=False)
|
||||
cmd = 'usermod -S '
|
||||
cmd += ','.join([g for g in out.split() if g != str(name)])
|
||||
cmd += ' {0}'.format(username)
|
||||
retcode = __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
else:
|
||||
log.error('group.deluser is not yet supported on this platform')
|
||||
return False
|
||||
return not retcode
|
||||
else:
|
||||
return True
|
||||
|
@ -198,9 +213,14 @@ def members(name, members_list):
|
|||
Replaces a membership list for a local group 'foo'.
|
||||
foo:x:1234:user1,user2,user3,...
|
||||
'''
|
||||
on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5'
|
||||
|
||||
if __grains__['kernel'] == 'Linux':
|
||||
retcode = __salt__['cmd.retcode']('gpasswd --members {0} {1}'.format(
|
||||
members_list, name), python_shell=False)
|
||||
if on_redhat_5:
|
||||
cmd = 'gpasswd -M {0} {1}'.format(members_list, name)
|
||||
else:
|
||||
cmd = 'gpasswd --members {0} {1}'.format(members_list, name)
|
||||
retcode = __salt__['cmd.retcode'](cmd, python_shell=False)
|
||||
elif __grains__['kernel'] == 'OpenBSD':
|
||||
retcode = 1
|
||||
grp_info = __salt__['group.info'](name)
|
||||
|
@ -219,5 +239,8 @@ def members(name, members_list):
|
|||
# provided list is '': users previously deleted from group
|
||||
else:
|
||||
retcode = 0
|
||||
else:
|
||||
log.error('group.members is not yet supported on this platform')
|
||||
return False
|
||||
|
||||
return not retcode
|
||||
|
|
|
@ -8,6 +8,12 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
import re
|
||||
import os
|
||||
HAS_DBUS = False
|
||||
try:
|
||||
import dbus
|
||||
HAS_DBUS = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
@ -25,38 +31,55 @@ def __virtual__():
|
|||
'''
|
||||
Only work on POSIX-like systems
|
||||
'''
|
||||
if HAS_DBUS is False and _uses_dbus():
|
||||
return False
|
||||
if salt.utils.is_windows():
|
||||
return False
|
||||
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def _parse_localectl():
|
||||
def _uses_dbus():
|
||||
if 'Arch' in __grains__['os_family']:
|
||||
return True
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
return False
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
return False
|
||||
elif 'Gentoo' in __grains__['os_family']:
|
||||
return False
|
||||
|
||||
|
||||
def _parse_dbus_locale():
|
||||
'''
|
||||
Get the 'System Locale' parameters from localectl
|
||||
Get the 'System Locale' parameters from dbus
|
||||
'''
|
||||
ret = {}
|
||||
for line in __salt__['cmd.run']('localectl').splitlines():
|
||||
cols = [x.strip() for x in line.split(':', 1)]
|
||||
if len(cols) > 1:
|
||||
cur_param = cols.pop(0)
|
||||
if cur_param == 'System Locale':
|
||||
try:
|
||||
key, val = re.match('^([A-Z_]+)=(.*)$', cols[0]).groups()
|
||||
except AttributeError:
|
||||
log.error('Odd locale parameter "{0}" detected in localectl '
|
||||
'output. This should not happen. localectl should '
|
||||
'catch this. You should probably investigate what '
|
||||
'caused this.'.format(cols[0]))
|
||||
else:
|
||||
ret[key] = val.replace('"', '')
|
||||
|
||||
bus = dbus.SystemBus()
|
||||
localed = bus.get_object('org.freedesktop.locale1',
|
||||
'/org/freedesktop/locale1')
|
||||
properties = dbus.Interface(localed, 'org.freedesktop.DBus.Properties')
|
||||
system_locale = properties.Get('org.freedesktop.locale1', 'Locale')
|
||||
|
||||
try:
|
||||
key, val = re.match('^([A-Z_]+)=(.*)$', system_locale[0]).groups()
|
||||
except AttributeError:
|
||||
log.error('Odd locale parameter "{0}" detected in dbus locale '
|
||||
'output. This should not happen. You should '
|
||||
'probably investigate what caused this.'.format(
|
||||
system_locale[0]))
|
||||
else:
|
||||
ret[key] = val.replace('"', '')
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def _localectl_get():
|
||||
def _locale_get():
|
||||
'''
|
||||
Use systemd's localectl command to get the current locale
|
||||
Use dbus to get the current locale
|
||||
'''
|
||||
return _parse_localectl().get('LANG', '')
|
||||
return _parse_dbus_locale().get('LANG', '')
|
||||
|
||||
|
||||
def _localectl_set(locale=''):
|
||||
|
@ -64,7 +87,7 @@ def _localectl_set(locale=''):
|
|||
Use systemd's localectl command to set the LANG locale parameter, making
|
||||
sure not to trample on other params that have been set.
|
||||
'''
|
||||
locale_params = _parse_localectl()
|
||||
locale_params = _parse_dbus_locale()
|
||||
locale_params['LANG'] = str(locale)
|
||||
args = ' '.join(['{0}="{1}"'.format(k, v)
|
||||
for k, v in six.iteritems(locale_params)])
|
||||
|
@ -99,7 +122,7 @@ def get_locale():
|
|||
'''
|
||||
cmd = ''
|
||||
if 'Arch' in __grains__['os_family']:
|
||||
return _localectl_get()
|
||||
return _locale_get()
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
cmd = 'grep "^LANG=" /etc/sysconfig/i18n'
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
|
@ -230,10 +253,12 @@ def gen_locale(locale, **kwargs):
|
|||
locale_info['territory']) in os.listdir(search)
|
||||
except OSError as ex:
|
||||
log.error(ex)
|
||||
raise CommandExecutionError("Locale \"{0}\" is not available.".format(locale))
|
||||
raise CommandExecutionError(
|
||||
"Locale \"{0}\" is not available.".format(locale))
|
||||
|
||||
if not valid:
|
||||
log.error('The provided locale "{0}" is not found in {1}'.format(locale, search))
|
||||
log.error(
|
||||
'The provided locale "{0}" is not found in {1}'.format(locale, search))
|
||||
return False
|
||||
|
||||
if os.path.exists('/etc/locale.gen'):
|
||||
|
@ -261,7 +286,8 @@ def gen_locale(locale, **kwargs):
|
|||
cmd.append(locale)
|
||||
elif salt.utils.which("localedef") is not None:
|
||||
cmd = ['localedef', '--force',
|
||||
'-i', "{0}_{1}".format(locale_info['language'], locale_info['territory']),
|
||||
'-i', "{0}_{1}".format(locale_info['language'],
|
||||
locale_info['territory']),
|
||||
'-f', locale_info['codeset'],
|
||||
locale]
|
||||
cmd.append(kwargs.get('verbose', False) and '--verbose' or '--quiet')
|
||||
|
|
|
@ -866,7 +866,8 @@ def _network_conf(conf_tuples=None, **kwargs):
|
|||
# (lxc.network.ipv4.gateway: auto)
|
||||
if (
|
||||
distutils.version.LooseVersion(version()) <= '1.0.7' and
|
||||
True not in ['lxc.network.ipv4.gateway' in a for a in ret]
|
||||
True not in ['lxc.network.ipv4.gateway' in a for a in ret] and
|
||||
True in ['lxc.network.ipv4' in a for a in ret]
|
||||
):
|
||||
ret.append({'lxc.network.ipv4.gateway': 'auto'})
|
||||
return ret
|
||||
|
|
|
@ -50,14 +50,25 @@ import salt.ext.six as six
|
|||
# pylint: disable=import-error
|
||||
from salt.ext.six.moves import range, zip # pylint: disable=no-name-in-module,redefined-builtin
|
||||
try:
|
||||
# Try to import MySQLdb
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
import MySQLdb.converters
|
||||
from MySQLdb.constants import FIELD_TYPE, FLAG
|
||||
HAS_MYSQLDB = True
|
||||
except ImportError:
|
||||
HAS_MYSQLDB = False
|
||||
# pylint: enable=import-error
|
||||
try:
|
||||
# MySQLdb import failed, try to import PyMySQL
|
||||
import pymysql
|
||||
pymysql.install_as_MySQLdb()
|
||||
import MySQLdb
|
||||
import MySQLdb.cursors
|
||||
import MySQLdb.converters
|
||||
from MySQLdb.constants import FIELD_TYPE, FLAG
|
||||
HAS_MYSQLDB = True
|
||||
except ImportError:
|
||||
# No MySQL Connector installed, return False
|
||||
HAS_MYSQLDB = False
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -133,8 +133,6 @@ def install(pkg=None,
|
|||
cmd.append(pkg)
|
||||
elif pkgs:
|
||||
cmd.extend(pkgs)
|
||||
else:
|
||||
return 'No package name specified'
|
||||
|
||||
if env is None:
|
||||
env = {}
|
||||
|
|
|
@ -457,6 +457,15 @@ def delete_key_recursive(hive, key):
|
|||
A dictionary listing the keys that deleted successfully as well as those
|
||||
that failed to delete.
|
||||
:rtype: dict
|
||||
|
||||
The following example will remove ``salt`` and all its subkeys from the
|
||||
``SOFTWARE`` key in ``HKEY_LOCAL_MACHINE``:
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' reg.delete_key_recursive HKLM SOFTWARE\\salt
|
||||
'''
|
||||
# Functions for traversing the registry tree
|
||||
def subkeys(key):
|
||||
|
|
|
@ -50,7 +50,7 @@ _ETHTOOL_CONFIG_OPTS = [
|
|||
'gso', 'gro', 'lro'
|
||||
]
|
||||
_RH_CONFIG_OPTS = [
|
||||
'domain', 'peerdns', 'defroute',
|
||||
'domain', 'peerdns', 'peerntp', 'defroute',
|
||||
'mtu', 'static-routes', 'gateway'
|
||||
]
|
||||
_RH_CONFIG_BONDING_OPTS = [
|
||||
|
@ -646,7 +646,7 @@ def _parse_settings_eth(opts, iface_type, enabled, iface):
|
|||
result['enable_ipv6'] = opts['enable_ipv6']
|
||||
|
||||
valid = _CONFIG_TRUE + _CONFIG_FALSE
|
||||
for opt in ['onparent', 'peerdns', 'slave', 'vlan', 'defroute', 'stp']:
|
||||
for opt in ['onparent', 'peerdns', 'peerntp', 'slave', 'vlan', 'defroute', 'stp']:
|
||||
if opt in opts:
|
||||
if opts[opt] in _CONFIG_TRUE:
|
||||
result[opt] = 'yes'
|
||||
|
|
|
@ -6,8 +6,10 @@ from __future__ import absolute_import
|
|||
|
||||
# Import python libs
|
||||
import os
|
||||
import errno
|
||||
import logging
|
||||
import re
|
||||
import string
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
@ -25,6 +27,67 @@ def __virtual__():
|
|||
return True
|
||||
|
||||
|
||||
def _get_zone_solaris():
|
||||
tzfile = '/etc/TIMEZONE'
|
||||
with salt.utils.fopen(tzfile, 'r') as fp_:
|
||||
for line in fp_:
|
||||
if 'TZ=' in line:
|
||||
zonepart = line.rstrip('\n').split('=')[-1]
|
||||
return zonepart.strip('\'"') or 'UTC'
|
||||
raise CommandExecutionError('Unable to get timezone from ' + tzfile)
|
||||
|
||||
|
||||
def _get_zone_sysconfig():
|
||||
tzfile = '/etc/sysconfig/clock'
|
||||
with salt.utils.fopen(tzfile, 'r') as fp_:
|
||||
for line in fp_:
|
||||
if re.match(r'^\s*#', line):
|
||||
continue
|
||||
if 'ZONE' in line and '=' in line:
|
||||
zonepart = line.rstrip('\n').split('=')[-1]
|
||||
return zonepart.strip('\'"') or 'UTC'
|
||||
raise CommandExecutionError('Unable to get timezone from ' + tzfile)
|
||||
|
||||
|
||||
def _get_zone_etc_localtime():
|
||||
tzfile = '/etc/localtime'
|
||||
tzdir = '/usr/share/zoneinfo/'
|
||||
tzdir_len = len(tzdir)
|
||||
try:
|
||||
olson_name = os.path.normpath(
|
||||
os.path.join('/etc', os.readlink(tzfile))
|
||||
)
|
||||
if olson_name.startswith(tzdir):
|
||||
return olson_name[tzdir_len:]
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
raise CommandExecutionError(tzfile + ' does not exist')
|
||||
elif exc.errno == errno.EINVAL:
|
||||
log.warning(
|
||||
tzfile + ' is not a symbolic link, attempting to match ' +
|
||||
tzfile + ' to zoneinfo files'
|
||||
)
|
||||
# Regular file. Try to match the hash.
|
||||
hash_type = __opts__.get('hash_type', 'md5')
|
||||
tzfile_hash = salt.utils.get_hash(tzfile, hash_type)
|
||||
# Not a link, just a copy of the tzdata file
|
||||
for root, dirs, files in os.walk(tzdir):
|
||||
for filename in files:
|
||||
full_path = os.path.join(root, filename)
|
||||
olson_name = full_path[tzdir_len:]
|
||||
if olson_name[0] in string.ascii_lowercase:
|
||||
continue
|
||||
if tzfile_hash == \
|
||||
salt.utils.get_hash(full_path, hash_type):
|
||||
return olson_name
|
||||
raise CommandExecutionError('Unable to determine timezone')
|
||||
|
||||
|
||||
def _get_zone_etc_timezone():
|
||||
with salt.utils.fopen('/etc/timezone', 'r') as fp_:
|
||||
return fp_.read().strip()
|
||||
|
||||
|
||||
def get_zone():
|
||||
'''
|
||||
Get current timezone (i.e. America/Denver)
|
||||
|
@ -37,7 +100,7 @@ def get_zone():
|
|||
'''
|
||||
cmd = ''
|
||||
if salt.utils.which('timedatectl'):
|
||||
out = __salt__['cmd.run']('timedatectl')
|
||||
out = __salt__['cmd.run'](['timedatectl'], python_shell=False)
|
||||
for line in (x.strip() for x in out.splitlines()):
|
||||
try:
|
||||
return re.match(r'Time ?zone:\s+(\S+)', line).group(1)
|
||||
|
@ -46,23 +109,21 @@ def get_zone():
|
|||
raise CommandExecutionError(
|
||||
'Failed to parse timedatectl output, this is likely a bug'
|
||||
)
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"'
|
||||
elif 'Suse' in __grains__['os_family']:
|
||||
cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"'
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
with salt.utils.fopen('/etc/timezone', 'r') as ofh:
|
||||
return ofh.read().strip()
|
||||
elif 'Gentoo' in __grains__['os_family']:
|
||||
with salt.utils.fopen('/etc/timezone', 'r') as ofh:
|
||||
return ofh.read().strip()
|
||||
elif __grains__['os_family'] in ('FreeBSD', 'OpenBSD', 'NetBSD'):
|
||||
return os.readlink('/etc/localtime').lstrip('/usr/share/zoneinfo/')
|
||||
elif 'Solaris' in __grains__['os_family']:
|
||||
cmd = 'grep "TZ=" /etc/TIMEZONE'
|
||||
out = __salt__['cmd.run'](cmd, python_shell=True).split('=')
|
||||
ret = out[1].replace('"', '')
|
||||
return ret
|
||||
else:
|
||||
if __grains__['os'].lower() == 'centos':
|
||||
return _get_zone_etc_localtime()
|
||||
os_family = __grains__['os_family']
|
||||
for family in ('RedHat', 'Suse'):
|
||||
if family in os_family:
|
||||
return _get_zone_sysconfig()
|
||||
for family in ('Debian', 'Gentoo'):
|
||||
if family in os_family:
|
||||
return _get_zone_etc_timezone()
|
||||
if os_family in ('FreeBSD', 'OpenBSD', 'NetBSD'):
|
||||
return _get_zone_etc_localtime()
|
||||
elif 'Solaris' in os_family:
|
||||
return _get_zone_solaris()
|
||||
raise CommandExecutionError('Unable to get timezone')
|
||||
|
||||
|
||||
def get_zonecode():
|
||||
|
@ -75,9 +136,7 @@ def get_zonecode():
|
|||
|
||||
salt '*' timezone.get_zonecode
|
||||
'''
|
||||
cmd = 'date +%Z'
|
||||
out = __salt__['cmd.run'](cmd)
|
||||
return out
|
||||
return __salt__['cmd.run'](['date', '+%Z'], python_shell=False)
|
||||
|
||||
|
||||
def get_offset():
|
||||
|
@ -90,9 +149,7 @@ def get_offset():
|
|||
|
||||
salt '*' timezone.get_offset
|
||||
'''
|
||||
cmd = 'date +%z'
|
||||
out = __salt__['cmd.run'](cmd)
|
||||
return out
|
||||
return __salt__['cmd.run'](['date', '+%z'], python_shell=False)
|
||||
|
||||
|
||||
def set_zone(timezone):
|
||||
|
@ -101,7 +158,8 @@ def set_zone(timezone):
|
|||
|
||||
The timezone is crucial to several system processes, each of which SHOULD
|
||||
be restarted (for instance, whatever you system uses as its cron and
|
||||
syslog daemons). This will not be magically done for you!
|
||||
syslog daemons). This will not be automagically done and must be done
|
||||
manually!
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -201,7 +259,7 @@ def get_hwclock():
|
|||
'''
|
||||
cmd = ''
|
||||
if salt.utils.which('timedatectl'):
|
||||
out = __salt__['cmd.run']('timedatectl')
|
||||
out = __salt__['cmd.run'](['timedatectl'], python_shell=False)
|
||||
for line in (x.strip() for x in out.splitlines()):
|
||||
if 'rtc in local tz' in line.lower():
|
||||
try:
|
||||
|
@ -214,39 +272,64 @@ def get_hwclock():
|
|||
raise CommandExecutionError(
|
||||
'Failed to parse timedatectl output, this is likely a bug'
|
||||
)
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
cmd = 'tail -n 1 /etc/adjtime'
|
||||
return __salt__['cmd.run'](cmd)
|
||||
elif 'Suse' in __grains__['os_family']:
|
||||
cmd = 'tail -n 1 /etc/adjtime'
|
||||
return __salt__['cmd.run'](cmd)
|
||||
elif 'Debian' in __grains__['os_family']:
|
||||
#Original way to look up hwclock on Debian-based systems
|
||||
cmd = 'grep "UTC=" /etc/default/rcS | grep -vE "^#"'
|
||||
out = __salt__['cmd.run'](
|
||||
cmd, ignore_retcode=True, python_shell=True).split('=')
|
||||
if len(out) > 1:
|
||||
if out[1] == 'yes':
|
||||
return 'UTC'
|
||||
else:
|
||||
return 'localtime'
|
||||
else:
|
||||
#Since Wheezy
|
||||
cmd = 'tail -n 1 /etc/adjtime'
|
||||
return __salt__['cmd.run'](cmd)
|
||||
elif 'Gentoo' in __grains__['os_family']:
|
||||
cmd = 'grep "^clock=" /etc/conf.d/hwclock | grep -vE "^#"'
|
||||
out = __salt__['cmd.run'](cmd, python_shell=True).split('=')
|
||||
return out[1].replace('"', '')
|
||||
elif 'Solaris' in __grains__['os_family']:
|
||||
if os.path.isfile('/etc/rtc_config'):
|
||||
with salt.utils.fopen('/etc/rtc_config', 'r') as fp_:
|
||||
for line in fp_:
|
||||
if line.startswith('zone_info=GMT'):
|
||||
return 'UTC'
|
||||
return 'localtime'
|
||||
else:
|
||||
return 'UTC'
|
||||
else:
|
||||
os_family = __grains__['os_family']
|
||||
for family in ('RedHat', 'Suse'):
|
||||
if family in os_family:
|
||||
cmd = ['tail', '-n', '1', '/etc/adjtime']
|
||||
return __salt__['cmd.run'](cmd, python_shell=False)
|
||||
if 'Debian' in __grains__['os_family']:
|
||||
# Original way to look up hwclock on Debian-based systems
|
||||
try:
|
||||
with salt.utils.fopen('/etc/default/rcS', 'r') as fp_:
|
||||
for line in fp_:
|
||||
if re.match(r'^\s*#', line):
|
||||
continue
|
||||
if 'UTC=' in line:
|
||||
is_utc = line.rstrip('\n').split('=')[-1].lower()
|
||||
if is_utc == 'yes':
|
||||
return 'UTC'
|
||||
else:
|
||||
return 'localtime'
|
||||
except IOError as exc:
|
||||
pass
|
||||
# Since Wheezy
|
||||
cmd = ['tail', '-n', '1', '/etc/adjtime']
|
||||
return __salt__['cmd.run'](cmd, python_shell=False)
|
||||
elif 'Gentoo' in __grains__['os_family']:
|
||||
offset_file = '/etc/conf.d/hwclock'
|
||||
try:
|
||||
with salt.utils.fopen(offset_file, 'r') as fp_:
|
||||
for line in fp_:
|
||||
if line.startswith('clock='):
|
||||
line = line.rstrip('\n')
|
||||
return line.split('=')[-1].strip('\'"')
|
||||
raise CommandExecutionError(
|
||||
'Offset information not found in {0}'.format(
|
||||
offset_file
|
||||
)
|
||||
)
|
||||
except IOError as exc:
|
||||
raise CommandExecutionError(
|
||||
'Problem reading offset file {0}: {1}'
|
||||
.format(offset_file, exc.strerror)
|
||||
)
|
||||
elif 'Solaris' in __grains__['os_family']:
|
||||
offset_file = '/etc/rtc_config'
|
||||
try:
|
||||
with salt.utils.fopen(offset_file, 'r') as fp_:
|
||||
for line in fp_:
|
||||
if line.startswith('zone_info=GMT'):
|
||||
return 'UTC'
|
||||
return 'localtime'
|
||||
except IOError as exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
# offset file does not exist
|
||||
return 'UTC'
|
||||
raise CommandExecutionError(
|
||||
'Problem reading offset file {0}: {1}'
|
||||
.format(offset_file, exc.strerror)
|
||||
)
|
||||
|
||||
|
||||
def set_hwclock(clock):
|
||||
|
@ -262,33 +345,31 @@ def set_hwclock(clock):
|
|||
timezone = get_zone()
|
||||
|
||||
if 'Solaris' in __grains__['os_family']:
|
||||
if clock.lower() not in ('localtime', 'utc'):
|
||||
raise SaltInvocationError(
|
||||
'localtime and UTC are the only permitted values'
|
||||
)
|
||||
if 'sparc' in __grains__['cpuarch']:
|
||||
return 'UTC is the only choice for SPARC architecture'
|
||||
if clock == 'localtime':
|
||||
cmd = 'rtc -z {0}'.format(timezone)
|
||||
__salt__['cmd.run'](cmd)
|
||||
return True
|
||||
elif clock == 'UTC':
|
||||
cmd = 'rtc -z GMT'
|
||||
__salt__['cmd.run'](cmd)
|
||||
return True
|
||||
else:
|
||||
zonepath = '/usr/share/zoneinfo/{0}'.format(timezone)
|
||||
raise SaltInvocationError(
|
||||
'UTC is the only choice for SPARC architecture'
|
||||
)
|
||||
cmd = ['rtc', '-z', 'GMT' if clock.lower() == 'utc' else timezone]
|
||||
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
|
||||
|
||||
zonepath = '/usr/share/zoneinfo/{0}'.format(timezone)
|
||||
|
||||
if not os.path.exists(zonepath):
|
||||
return 'Zone does not exist: {0}'.format(zonepath)
|
||||
raise CommandExecutionError(
|
||||
'Zone \'{0}\' does not exist'.format(zonepath)
|
||||
)
|
||||
|
||||
if 'Solaris' not in __grains__['os_family']:
|
||||
os.unlink('/etc/localtime')
|
||||
os.symlink(zonepath, '/etc/localtime')
|
||||
os.unlink('/etc/localtime')
|
||||
os.symlink(zonepath, '/etc/localtime')
|
||||
|
||||
if 'Arch' in __grains__['os_family']:
|
||||
if clock == 'localtime':
|
||||
cmd = 'timezonectl set-local-rtc true'
|
||||
__salt__['cmd.run'](cmd)
|
||||
else:
|
||||
cmd = 'timezonectl set-local-rtc false'
|
||||
__salt__['cmd.run'](cmd)
|
||||
cmd = ['timezonectl', 'set-local-rtc',
|
||||
'true' if clock == 'localtime' else 'false']
|
||||
return __salt__['cmd.retcode'](cmd, python_shell=False) == 0
|
||||
elif 'RedHat' in __grains__['os_family']:
|
||||
__salt__['file.sed'](
|
||||
'/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone))
|
||||
|
|
|
@ -17,7 +17,7 @@ import copy
|
|||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.ext.six import string_types
|
||||
from salt.ext import six
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -68,6 +68,27 @@ def _build_gecos(gecos_dict):
|
|||
gecos_dict.get('homephone', ''))
|
||||
|
||||
|
||||
def _update_gecos(name, key, value):
|
||||
'''
|
||||
Common code to change a user's GECOS information
|
||||
'''
|
||||
if value is None:
|
||||
value = ''
|
||||
elif not isinstance(value, six.string_types):
|
||||
value = str(value)
|
||||
pre_info = _get_gecos(name)
|
||||
if not pre_info:
|
||||
return False
|
||||
if value == pre_info[key]:
|
||||
return True
|
||||
gecos_data = copy.deepcopy(pre_info)
|
||||
gecos_data[key] = value
|
||||
cmd = ['usermod', '-c', _build_gecos(gecos_data), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
return _get_gecos(name).get(key) == value
|
||||
|
||||
|
||||
def add(name,
|
||||
uid=None,
|
||||
gid=None,
|
||||
|
@ -99,47 +120,51 @@ def add(name,
|
|||
if gid not in (None, ''):
|
||||
cmd.extend(['-g', str(gid)])
|
||||
elif groups is not None and name in groups:
|
||||
defs_file = '/etc/login.defs'
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
try:
|
||||
for line in salt.utils.fopen('/etc/login.defs'):
|
||||
if 'USERGROUPS_ENAB' not in line[:15]:
|
||||
continue
|
||||
with salt.utils.fopen(defs_file) as fp_:
|
||||
for line in fp_:
|
||||
if 'USERGROUPS_ENAB' not in line[:15]:
|
||||
continue
|
||||
|
||||
if 'yes' in line:
|
||||
cmd.extend([
|
||||
'-g', str(__salt__['file.group_to_gid'](name))
|
||||
])
|
||||
if 'yes' in line:
|
||||
cmd.extend([
|
||||
'-g', str(__salt__['file.group_to_gid'](name))
|
||||
])
|
||||
|
||||
# We found what we wanted, let's break out of the loop
|
||||
break
|
||||
# We found what we wanted, let's break out of the loop
|
||||
break
|
||||
except OSError:
|
||||
log.debug('Error reading /etc/login.defs', exc_info=True)
|
||||
log.debug(
|
||||
'Error reading ' + defs_file,
|
||||
exc_info_on_loglevel=logging.DEBUG
|
||||
)
|
||||
else:
|
||||
usermgmt_file = '/etc/usermgmt.conf'
|
||||
try:
|
||||
for line in salt.utils.fopen('/etc/usermgmt.conf'):
|
||||
if 'group' not in line[:5]:
|
||||
continue
|
||||
with salt.utils.fopen(usermgmt_file) as fp_:
|
||||
for line in fp_:
|
||||
if 'group' not in line[:5]:
|
||||
continue
|
||||
|
||||
for val in line.split(" "):
|
||||
cmd.extend([
|
||||
'-g', str(val[1])
|
||||
])
|
||||
for val in line.split(' '):
|
||||
cmd.extend([
|
||||
'-g', str(val[1])
|
||||
])
|
||||
|
||||
# We found what we wanted, let's break out of the loop
|
||||
break
|
||||
# We found what we wanted, let's break out of the loop
|
||||
break
|
||||
except OSError:
|
||||
# /etc/usermgmt.conf not present: defaults will be used
|
||||
pass
|
||||
|
||||
if isinstance(createhome, bool):
|
||||
if salt.utils.is_true(createhome):
|
||||
if createhome:
|
||||
cmd.append('-m')
|
||||
elif (__grains__['kernel'] != 'NetBSD'
|
||||
and __grains__['kernel'] != 'OpenBSD'):
|
||||
cmd.append('-M')
|
||||
else:
|
||||
log.error('Value passes to ``createhome`` must be a boolean')
|
||||
return False
|
||||
|
||||
if home is not None:
|
||||
cmd.extend(['-d', home])
|
||||
|
@ -220,7 +245,7 @@ def delete(name, remove=False, force=False):
|
|||
if RETCODE_12_ERROR_REGEX.match(ret['stderr']) is not None:
|
||||
# We've hit the bug, let's log it and not fail
|
||||
log.debug(
|
||||
'While the userdel exited with code 12, this is a know bug on '
|
||||
'While the userdel exited with code 12, this is a known bug on '
|
||||
'debian based distributions. See http://goo.gl/HH3FzT'
|
||||
)
|
||||
return True
|
||||
|
@ -263,10 +288,7 @@ def chuid(name, uid):
|
|||
return True
|
||||
cmd = ['usermod', '-u', '{0}'.format(uid), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['uid'] != pre_info['uid']:
|
||||
return post_info['uid'] == uid
|
||||
return False
|
||||
return info(name).get('uid') == uid
|
||||
|
||||
|
||||
def chgid(name, gid):
|
||||
|
@ -284,10 +306,7 @@ def chgid(name, gid):
|
|||
return True
|
||||
cmd = ['usermod', '-g', '{0}'.format(gid), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['gid'] != pre_info['gid']:
|
||||
return post_info['gid'] == gid
|
||||
return False
|
||||
return info(name).get('gid') == gid
|
||||
|
||||
|
||||
def chshell(name, shell):
|
||||
|
@ -305,10 +324,7 @@ def chshell(name, shell):
|
|||
return True
|
||||
cmd = ['usermod', '-s', shell, name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['shell'] != pre_info['shell']:
|
||||
return post_info['shell'] == shell
|
||||
return False
|
||||
return info(name).get('shell') == shell
|
||||
|
||||
|
||||
def chhome(name, home, persist=False):
|
||||
|
@ -325,58 +341,64 @@ def chhome(name, home, persist=False):
|
|||
pre_info = info(name)
|
||||
if home == pre_info['home']:
|
||||
return True
|
||||
cmd = 'usermod -d {0} '.format(home)
|
||||
cmd = ['usermod', '-d', '{0}'.format(home)]
|
||||
if persist and __grains__['kernel'] != 'OpenBSD':
|
||||
cmd += ' -m '
|
||||
cmd += name
|
||||
cmd.append('-m')
|
||||
cmd.append(name)
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['home'] != pre_info['home']:
|
||||
return post_info['home'] == home
|
||||
return False
|
||||
return info(name).get('home') == home
|
||||
|
||||
|
||||
def chgroups(name, groups, append=False):
|
||||
'''
|
||||
Change the groups this user belongs to, add append to append the specified
|
||||
groups
|
||||
Change the groups to which this user belongs
|
||||
|
||||
CLI Example:
|
||||
name
|
||||
User to modify
|
||||
|
||||
groups
|
||||
Groups to set for the user
|
||||
|
||||
append : False
|
||||
If ``True``, append the specified group(s). Otherwise, this function
|
||||
will replace the user's groups with the specified group(s).
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chgroups foo wheel,root True
|
||||
salt '*' user.chgroups foo wheel,root
|
||||
salt '*' user.chgroups foo wheel,root append=True
|
||||
'''
|
||||
if isinstance(groups, string_types):
|
||||
if isinstance(groups, six.string_types):
|
||||
groups = groups.split(',')
|
||||
ugrps = set(list_groups(name))
|
||||
if ugrps == set(groups):
|
||||
return True
|
||||
cmd = 'usermod '
|
||||
cmd = ['usermod']
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
if append:
|
||||
cmd += '-a '
|
||||
cmd.append('-a')
|
||||
else:
|
||||
if append:
|
||||
cmd += '-G '
|
||||
cmd.append('-G')
|
||||
else:
|
||||
cmd += '-S '
|
||||
cmd.append('-S')
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
cmd += '-G '
|
||||
cmd += '"{0}" {1}'.format(','.join(groups), name)
|
||||
cmdret = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
ret = not cmdret['retcode']
|
||||
cmd.append('-G')
|
||||
cmd.extend([','.join(groups), name])
|
||||
result = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
# try to fallback on gpasswd to add user to localgroups
|
||||
# for old lib-pamldap support
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
if not ret and ('not found in' in cmdret['stderr']):
|
||||
if result['retcode'] != 0 and 'not found in' in result['stderr']:
|
||||
ret = True
|
||||
for group in groups:
|
||||
cmd = 'gpasswd -a {0} {1}'.format(name, group)
|
||||
cmdret = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
if cmdret['retcode']:
|
||||
cmd = ['gpasswd', '-a', '{0}'.format(name), '{1}'.format(group)]
|
||||
if __salt__['cmd.retcode'](cmd, python_shell=False) != 0:
|
||||
ret = False
|
||||
return ret
|
||||
return ret
|
||||
return result['retcode'] == 0
|
||||
|
||||
|
||||
def chfullname(name, fullname):
|
||||
|
@ -389,24 +411,7 @@ def chfullname(name, fullname):
|
|||
|
||||
salt '*' user.chfullname foo "Foo Bar"
|
||||
'''
|
||||
if fullname is None:
|
||||
fullname = ''
|
||||
else:
|
||||
fullname = str(fullname)
|
||||
|
||||
pre_info = _get_gecos(name)
|
||||
if not pre_info:
|
||||
return False
|
||||
if fullname == pre_info['fullname']:
|
||||
return True
|
||||
gecos_field = copy.deepcopy(pre_info)
|
||||
gecos_field['fullname'] = fullname
|
||||
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['fullname'] != pre_info['fullname']:
|
||||
return post_info['fullname'] == fullname
|
||||
return False
|
||||
return _update_gecos(name, 'fullname', fullname)
|
||||
|
||||
|
||||
def chroomnumber(name, roomnumber):
|
||||
|
@ -419,24 +424,7 @@ def chroomnumber(name, roomnumber):
|
|||
|
||||
salt '*' user.chroomnumber foo 123
|
||||
'''
|
||||
if roomnumber is None:
|
||||
roomnumber = ''
|
||||
else:
|
||||
roomnumber = str(roomnumber)
|
||||
|
||||
pre_info = _get_gecos(name)
|
||||
if not pre_info:
|
||||
return False
|
||||
if roomnumber == pre_info['roomnumber']:
|
||||
return True
|
||||
gecos_field = copy.deepcopy(pre_info)
|
||||
gecos_field['roomnumber'] = roomnumber
|
||||
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['roomnumber'] != pre_info['roomnumber']:
|
||||
return post_info['roomnumber'] == roomnumber
|
||||
return False
|
||||
return _update_gecos(name, 'roomnumber', roomnumber)
|
||||
|
||||
|
||||
def chworkphone(name, workphone):
|
||||
|
@ -447,26 +435,9 @@ def chworkphone(name, workphone):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chworkphone foo "7735550123"
|
||||
salt '*' user.chworkphone foo 7735550123
|
||||
'''
|
||||
if workphone is None:
|
||||
workphone = ''
|
||||
else:
|
||||
workphone = str(workphone)
|
||||
|
||||
pre_info = _get_gecos(name)
|
||||
if not pre_info:
|
||||
return False
|
||||
if workphone == pre_info['workphone']:
|
||||
return True
|
||||
gecos_field = copy.deepcopy(pre_info)
|
||||
gecos_field['workphone'] = workphone
|
||||
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['workphone'] != pre_info['workphone']:
|
||||
return post_info['workphone'] == workphone
|
||||
return False
|
||||
return _update_gecos(name, 'workphone', workphone)
|
||||
|
||||
|
||||
def chhomephone(name, homephone):
|
||||
|
@ -477,32 +448,18 @@ def chhomephone(name, homephone):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.chhomephone foo "7735551234"
|
||||
salt '*' user.chhomephone foo 7735551234
|
||||
'''
|
||||
if homephone is None:
|
||||
homephone = ''
|
||||
else:
|
||||
homephone = str(homephone)
|
||||
|
||||
pre_info = _get_gecos(name)
|
||||
if not pre_info:
|
||||
return False
|
||||
if homephone == pre_info['homephone']:
|
||||
return True
|
||||
gecos_field = copy.deepcopy(pre_info)
|
||||
gecos_field['homephone'] = homephone
|
||||
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
post_info = info(name)
|
||||
if post_info['homephone'] != pre_info['homephone']:
|
||||
return post_info['homephone'] == homephone
|
||||
return False
|
||||
return _update_gecos(name, 'homephone', homephone)
|
||||
|
||||
|
||||
def chloginclass(name, loginclass):
|
||||
'''
|
||||
Change the default login class of the user
|
||||
|
||||
.. note::
|
||||
This function only applies to OpenBSD systems.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -511,15 +468,11 @@ def chloginclass(name, loginclass):
|
|||
'''
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
return False
|
||||
pre_info = get_loginclass(name)
|
||||
if loginclass == pre_info['loginclass']:
|
||||
if loginclass == get_loginclass(name):
|
||||
return True
|
||||
cmd = 'usermod -L {0} {1}'.format(loginclass, name)
|
||||
__salt__['cmd.run'](cmd)
|
||||
post_info = get_loginclass(name)
|
||||
if post_info['loginclass'] != pre_info['loginclass']:
|
||||
return post_info['loginclass'] == loginclass
|
||||
return False
|
||||
cmd = ['usermod', '-L', '{0}'.format(loginclass), '{0}'.format(name)]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
return get_loginclass(name) == loginclass
|
||||
|
||||
|
||||
def info(name):
|
||||
|
@ -544,6 +497,9 @@ def get_loginclass(name):
|
|||
'''
|
||||
Get the login class of the user
|
||||
|
||||
.. note::
|
||||
This function only applies to OpenBSD systems.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -552,15 +508,19 @@ def get_loginclass(name):
|
|||
'''
|
||||
if __grains__['kernel'] != 'OpenBSD':
|
||||
return False
|
||||
userinfo = __salt__['cmd.run_stdout']('userinfo {0}'.format(name),
|
||||
output_loglevel='debug')
|
||||
userinfo = __salt__['cmd.run_stdout'](
|
||||
['userinfo', name],
|
||||
python_shell=False)
|
||||
for line in userinfo.splitlines():
|
||||
if line.startswith("class"):
|
||||
loginclass = line.split()
|
||||
if len(loginclass) == 2:
|
||||
return {'loginclass': loginclass[1]}
|
||||
if line.startswith('class'):
|
||||
try:
|
||||
ret = line.split(None, 1)[1]
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
else:
|
||||
return {'loginclass': '""'}
|
||||
ret = ''
|
||||
return ret
|
||||
|
||||
|
||||
def _format_info(data):
|
||||
|
@ -624,13 +584,12 @@ def rename(name, new_name):
|
|||
'''
|
||||
current_info = info(name)
|
||||
if not current_info:
|
||||
raise CommandExecutionError('User {0!r} does not exist'.format(name))
|
||||
raise CommandExecutionError('User \'{0}\' does not exist'.format(name))
|
||||
new_info = info(new_name)
|
||||
if new_info:
|
||||
raise CommandExecutionError('User {0!r} already exists'.format(new_name))
|
||||
cmd = 'usermod -l {0} {1}'.format(new_name, name)
|
||||
__salt__['cmd.run'](cmd)
|
||||
post_info = info(new_name)
|
||||
if post_info['name'] != current_info['name']:
|
||||
return post_info['name'] == new_name
|
||||
return False
|
||||
raise CommandExecutionError(
|
||||
'User \'{0}\' already exists'.format(new_name)
|
||||
)
|
||||
cmd = ['usermod', '-l', '{0}'.format(new_name), '{0}'.format(name)]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
return info(name).get('name') == new_name
|
||||
|
|
|
@ -75,7 +75,11 @@ def get_path():
|
|||
'''
|
||||
ret = __salt__['reg.read_key']('HKEY_LOCAL_MACHINE',
|
||||
'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
|
||||
'PATH').split(';')
|
||||
'PATH')
|
||||
if isinstance(ret, dict):
|
||||
ret = ret['vdata'].split(';')
|
||||
if isinstance(ret, str):
|
||||
ret = ret.split(';')
|
||||
|
||||
# Trim ending backslash
|
||||
return list(map(_normalize_dir, ret))
|
||||
|
@ -148,11 +152,11 @@ def add(path, index=0):
|
|||
|
||||
# Add it to the Path
|
||||
sysPath.insert(index, path)
|
||||
regedit = __salt__['reg.set_key'](
|
||||
regedit = __salt__['reg.set_value'](
|
||||
'HKEY_LOCAL_MACHINE',
|
||||
'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
|
||||
'PATH',
|
||||
';'.join(sysPath),
|
||||
'PATH',
|
||||
'REG_EXPAND_SZ'
|
||||
)
|
||||
|
||||
|
@ -190,11 +194,11 @@ def remove(path):
|
|||
except ValueError:
|
||||
return True
|
||||
|
||||
regedit = __salt__['reg.set_key'](
|
||||
regedit = __salt__['reg.set_value'](
|
||||
'HKEY_LOCAL_MACHINE',
|
||||
'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
|
||||
'PATH',
|
||||
';'.join(sysPath),
|
||||
'PATH',
|
||||
'REG_EXPAND_SZ'
|
||||
)
|
||||
if regedit:
|
||||
|
|
|
@ -36,6 +36,7 @@ import shlex
|
|||
from salt.exceptions import CommandExecutionError, SaltRenderError
|
||||
import salt.utils
|
||||
import salt.syspaths
|
||||
from salt.exceptions import MinionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -612,7 +613,10 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs):
|
|||
if installer.startswith('salt:'):
|
||||
if __salt__['cp.hash_file'](installer, saltenv) != \
|
||||
__salt__['cp.hash_file'](cached_pkg):
|
||||
cached_pkg = __salt__['cp.cache_file'](installer, saltenv)
|
||||
try:
|
||||
cached_pkg = __salt__['cp.cache_file'](installer, saltenv)
|
||||
except MinionError as exc:
|
||||
return '{0}: {1}'.format(exc, installer)
|
||||
|
||||
# Check if the installer was cached successfully
|
||||
if not cached_pkg:
|
||||
|
|
|
@ -30,7 +30,10 @@ log = logging.getLogger(__name__)
|
|||
|
||||
try:
|
||||
import pywintypes
|
||||
import wmi
|
||||
import pythoncom
|
||||
import win32api
|
||||
import win32con
|
||||
import win32net
|
||||
import win32netcon
|
||||
import win32profile
|
||||
|
@ -802,15 +805,96 @@ def rename(name, new_name):
|
|||
|
||||
salt '*' user.rename jsnuffy jshmoe
|
||||
'''
|
||||
# Load information for the current name
|
||||
current_info = info(name)
|
||||
if not current_info:
|
||||
raise CommandExecutionError('User {0!r} does not exist'.format(name))
|
||||
|
||||
# Look for an existing user with the new name
|
||||
new_info = info(new_name)
|
||||
if new_info:
|
||||
raise CommandExecutionError('User {0!r} already exists'.format(new_name))
|
||||
cmd = 'wmic useraccount where name="{0}" rename {1}'.format(name, new_name)
|
||||
__salt__['cmd.run'](cmd)
|
||||
|
||||
# Rename the user account
|
||||
# Connect to WMI
|
||||
pythoncom.CoInitialize()
|
||||
c = wmi.WMI(find_classes=0)
|
||||
|
||||
# Get the user object
|
||||
try:
|
||||
user = c.Win32_UserAccount(Name=name)[0]
|
||||
except IndexError:
|
||||
raise CommandExecutionError('User {0!r} does not exist'.format(name))
|
||||
|
||||
# Rename the user
|
||||
result = user.Rename(new_name)[0]
|
||||
|
||||
# Check the result (0 means success)
|
||||
if not result == 0:
|
||||
# Define Error Dict
|
||||
error_dict = {0: 'Success',
|
||||
1: 'Instance not found',
|
||||
2: 'Instance required',
|
||||
3: 'Invalid parameter',
|
||||
4: 'User not found',
|
||||
5: 'Domain not found',
|
||||
6: 'Operation is allowed only on the primary domain controller of the domain',
|
||||
7: 'Operation is not allowed on the last administrative account',
|
||||
8: 'Operation is not allowed on specified special groups: user, admin, local, or guest',
|
||||
9: 'Other API error',
|
||||
10: 'Internal error'}
|
||||
raise CommandExecutionError('There was an error renaming {0!r} to {1!r}. Error: {2}'.format(name, new_name, error_dict[result]))
|
||||
|
||||
# Load information for the new name
|
||||
post_info = info(new_name)
|
||||
|
||||
# Verify that the name has changed
|
||||
if post_info['name'] != current_info['name']:
|
||||
return post_info['name'] == new_name
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def current(sam=False):
|
||||
'''
|
||||
Get the username that salt-minion is running under. If salt-minion is
|
||||
running as a service it should return the Local System account. If salt is
|
||||
running from a command prompt it should return the username that started the
|
||||
command prompt.
|
||||
|
||||
.. versionadded:: 2015.5.6
|
||||
|
||||
:param bool sam:
|
||||
False returns just the username without any domain notation. True
|
||||
returns the domain with the username in the SAM format. Ie:
|
||||
|
||||
``domain\\username``
|
||||
|
||||
:return:
|
||||
Returns False if the username cannot be returned. Otherwise returns the
|
||||
username.
|
||||
:rtype: bool str
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.current
|
||||
'''
|
||||
try:
|
||||
if sam:
|
||||
user_name = win32api.GetUserNameEx(win32con.NameSamCompatible)
|
||||
else:
|
||||
user_name = win32api.GetUserName()
|
||||
except pywintypes.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to get current user')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
if not user_name:
|
||||
return False
|
||||
|
||||
return user_name
|
||||
|
|
|
@ -738,13 +738,13 @@ def check_db(*names, **kwargs):
|
|||
__context__['pkg._avail'] = avail
|
||||
|
||||
ret = {}
|
||||
repoquery_cmd = repoquery_base + ' {0}'.format(" ".join(names))
|
||||
provides = sorted(
|
||||
set(x.name for x in _repoquery_pkginfo(repoquery_cmd))
|
||||
)
|
||||
for name in names:
|
||||
ret.setdefault(name, {})['found'] = name in avail
|
||||
if not ret[name]['found']:
|
||||
repoquery_cmd = repoquery_base + ' {0}'.format(name)
|
||||
provides = sorted(
|
||||
set(x.name for x in _repoquery_pkginfo(repoquery_cmd))
|
||||
)
|
||||
if name in provides:
|
||||
# Package was not in avail but was found by the repoquery_cmd
|
||||
ret[name]['found'] = True
|
||||
|
|
|
@ -118,24 +118,23 @@ The token may be sent in one of two ways:
|
|||
|
||||
For example, using curl:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
curl -sSk https://localhost:8000/login \
|
||||
-H 'Accept: application/x-yaml' \
|
||||
-d username=saltdev \
|
||||
-d password=saltdev \
|
||||
curl -sSk https://localhost:8000/login \\
|
||||
-H 'Accept: application/x-yaml' \\
|
||||
-d username=saltdev \\
|
||||
-d password=saltdev \\
|
||||
-d eauth=auto
|
||||
|
||||
Copy the ``token`` value from the output and include it in subsequent
|
||||
requests:
|
||||
Copy the ``token`` value from the output and include it in subsequent requests:
|
||||
|
||||
.. code-block:: bash
|
||||
.. code-block:: bash
|
||||
|
||||
curl -sSk https://localhost:8000 \
|
||||
-H 'Accept: application/x-yaml' \
|
||||
-H 'X-Auth-Token: 697adbdc8fe971d09ae4c2a3add7248859c87079'\
|
||||
-d client=local \
|
||||
-d tgt='*' \
|
||||
curl -sSk https://localhost:8000 \\
|
||||
-H 'Accept: application/x-yaml' \\
|
||||
-H 'X-Auth-Token: 697adbdc8fe971d09ae4c2a3add7248859c87079'\\
|
||||
-d client=local \\
|
||||
-d tgt='*' \\
|
||||
-d fun=test.ping
|
||||
|
||||
* Sent via a cookie. This option is a convenience for HTTP clients that
|
||||
|
@ -146,19 +145,19 @@ The token may be sent in one of two ways:
|
|||
.. code-block:: bash
|
||||
|
||||
# Write the cookie file:
|
||||
curl -sSk https://localhost:8000/login \
|
||||
-c ~/cookies.txt \
|
||||
-H 'Accept: application/x-yaml' \
|
||||
-d username=saltdev \
|
||||
-d password=saltdev \
|
||||
curl -sSk https://localhost:8000/login \\
|
||||
-c ~/cookies.txt \\
|
||||
-H 'Accept: application/x-yaml' \\
|
||||
-d username=saltdev \\
|
||||
-d password=saltdev \\
|
||||
-d eauth=auto
|
||||
|
||||
# Read the cookie file:
|
||||
curl -sSk https://localhost:8000 \
|
||||
-b ~/cookies.txt \
|
||||
-H 'Accept: application/x-yaml' \
|
||||
-d client=local \
|
||||
-d tgt='*' \
|
||||
curl -sSk https://localhost:8000 \\
|
||||
-b ~/cookies.txt \\
|
||||
-H 'Accept: application/x-yaml' \\
|
||||
-d client=local \\
|
||||
-d tgt='*' \\
|
||||
-d fun=test.ping
|
||||
|
||||
.. seealso:: You can bypass the session handling via the :py:class:`Run` URL.
|
||||
|
|
|
@ -122,7 +122,7 @@ def ext_pillar(minion_id,
|
|||
environment='base',
|
||||
prefix='',
|
||||
service_url=None,
|
||||
s3_cache_expire=30, # cache for 30 seconds
|
||||
s3_cache_expire=30, # cache for 30 seconds
|
||||
s3_sync_on_update=True): # sync cache on update rather than jit
|
||||
|
||||
'''
|
||||
|
@ -375,6 +375,8 @@ def _get_file_from_s3(creds, metadata, saltenv, bucket, path,
|
|||
|
||||
cached_md5 = salt.utils.get_hash(cached_file_path, 'md5')
|
||||
|
||||
log.debug("Cached file: path={0}, md5={1}, etag={2}".format(cached_file_path, cached_md5, file_md5))
|
||||
|
||||
# hashes match we have a cache hit
|
||||
log.debug("Cached file: path={0}, md5={1}, etag={2}".format(cached_file_path, cached_md5, file_md5))
|
||||
if cached_md5 == file_md5:
|
||||
|
|
|
@ -18,6 +18,7 @@ import bisect
|
|||
import salt.payload
|
||||
import salt.utils
|
||||
import salt.utils.jid
|
||||
import salt.exceptions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -73,12 +74,16 @@ def _walk_through(job_dir):
|
|||
|
||||
|
||||
#TODO: add to returner docs-- this is a new one
|
||||
def prep_jid(nocache=False, passed_jid=None):
|
||||
def prep_jid(nocache=False, passed_jid=None, recurse_count=0):
|
||||
'''
|
||||
Return a job id and prepare the job id directory
|
||||
This is the function responsible for making sure jids don't collide (unless its passed a jid)
|
||||
So do what you have to do to make sure that stays the case
|
||||
'''
|
||||
if recurse_count >= 5:
|
||||
err = 'prep_jid could not store a jid after {0} tries.'.format(recurse_count)
|
||||
log.error(err)
|
||||
raise salt.exceptions.SaltCacheError(err)
|
||||
if passed_jid is None: # this can be a None of an empty string
|
||||
jid = salt.utils.jid.gen_jid()
|
||||
else:
|
||||
|
@ -91,15 +96,22 @@ def prep_jid(nocache=False, passed_jid=None):
|
|||
try:
|
||||
os.makedirs(jid_dir_)
|
||||
except OSError:
|
||||
# TODO: some sort of sleep or something? Spinning is generally bad practice
|
||||
time.sleep(0.1)
|
||||
if passed_jid is None:
|
||||
recurse_count += recurse_count
|
||||
return prep_jid(nocache=nocache)
|
||||
|
||||
with salt.utils.fopen(os.path.join(jid_dir_, 'jid'), 'wb+') as fn_:
|
||||
fn_.write(jid)
|
||||
if nocache:
|
||||
with salt.utils.fopen(os.path.join(jid_dir_, 'nocache'), 'wb+') as fn_:
|
||||
fn_.write('')
|
||||
try:
|
||||
with salt.utils.fopen(os.path.join(jid_dir_, 'jid'), 'wb+') as fn_:
|
||||
fn_.write(jid)
|
||||
if nocache:
|
||||
with salt.utils.fopen(os.path.join(jid_dir_, 'nocache'), 'wb+') as fn_:
|
||||
fn_.write('')
|
||||
except IOError:
|
||||
log.warn('Could not write out jid file for job {0}. Retrying.'.format(jid))
|
||||
time.sleep(0.1)
|
||||
recurse_count += recurse_count
|
||||
return prep_jid(passed_jid=jid, nocache=nocache)
|
||||
|
||||
return jid
|
||||
|
||||
|
|
|
@ -170,95 +170,10 @@ def event(tagmatch='*', count=-1, quiet=False, sock_dir=None, pretty=False):
|
|||
echo $data | jq -colour-output .
|
||||
done
|
||||
|
||||
The following example monitors Salt's event bus in a background process
|
||||
watching for returns for a given job. Requires a POSIX environment and jq
|
||||
<http://stedolan.github.io/jq/>.
|
||||
.. seealso::
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
#!/bin/sh
|
||||
# Usage: ./eventlisten.sh '*' test.sleep 10
|
||||
|
||||
# Mimic fnmatch from the Python stdlib.
|
||||
fnmatch() { case "$2" in $1) return 0 ;; *) return 1 ;; esac ; }
|
||||
count() { printf '%s\n' "$#" ; }
|
||||
|
||||
listen() {
|
||||
events='events'
|
||||
mkfifo $events
|
||||
exec 3<>$events # Hold the fd open.
|
||||
|
||||
# Start listening to events before starting the command to avoid race
|
||||
# conditions.
|
||||
salt-run state.event count=-1 >&3 &
|
||||
events_pid=$!
|
||||
|
||||
(
|
||||
timeout=$(( 60 * 60 ))
|
||||
sleep $timeout
|
||||
kill -s USR2 $$
|
||||
) &
|
||||
timeout_pid=$!
|
||||
|
||||
# Give the runner a few to connect to the event bus.
|
||||
printf 'Subscribing to the Salt event bus...\n'
|
||||
sleep 4
|
||||
|
||||
trap '
|
||||
excode=$?; trap - EXIT;
|
||||
exec 3>&-
|
||||
kill '"${timeout_pid}"'
|
||||
kill '"${events_pid}"'
|
||||
rm '"${events}"'
|
||||
exit
|
||||
echo $excode
|
||||
' INT TERM EXIT
|
||||
|
||||
trap '
|
||||
printf '\''Timeout reached; exiting.\n'\''
|
||||
exit 4
|
||||
' USR2
|
||||
|
||||
# Run the command and get the JID.
|
||||
jid=$(salt --async "$@")
|
||||
jid="${jid#*: }" # Remove leading text up to the colon.
|
||||
|
||||
# Create the event tags to listen for.
|
||||
start_tag="salt/job/${jid}/new"
|
||||
ret_tag="salt/job/${jid}/ret/*"
|
||||
|
||||
# ``read`` will block when no events are going through the bus.
|
||||
printf 'Waiting for tag %s\n' "$ret_tag"
|
||||
while read -r tag data; do
|
||||
if fnmatch "$start_tag" "$tag"; then
|
||||
minions=$(printf '%s\n' "${data}" | jq -r '.["minions"][]')
|
||||
num_minions=$(count $minions)
|
||||
printf 'Waiting for %s minions.\n' "$num_minions"
|
||||
continue
|
||||
fi
|
||||
|
||||
if fnmatch "$ret_tag" "$tag"; then
|
||||
mid="${tag##*/}"
|
||||
printf 'Got return for %s.\n' "$mid"
|
||||
printf 'Pretty-printing event: %s\n' "$tag"
|
||||
printf '%s\n' "$data" | jq .
|
||||
|
||||
minions="$(printf '%s\n' "$minions" | sed -e '/'"$mid"'/d')"
|
||||
num_minions=$(count $minions)
|
||||
if [ $((num_minions)) -eq 0 ]; then
|
||||
printf 'All minions returned.\n'
|
||||
break
|
||||
else
|
||||
printf 'Remaining minions: %s\n' "$num_minions"
|
||||
fi
|
||||
else
|
||||
printf 'Skipping tag: %s\n' "$tag"
|
||||
continue
|
||||
fi
|
||||
done <&3
|
||||
}
|
||||
|
||||
listen "$@"
|
||||
See :glob:`tests/eventlisten.sh` for an example of usage within a shell
|
||||
script.
|
||||
'''
|
||||
sevent = salt.utils.event.get_event(
|
||||
'master',
|
||||
|
|
|
@ -812,6 +812,16 @@ def symlink(
|
|||
user = __opts__['user']
|
||||
|
||||
if salt.utils.is_windows():
|
||||
|
||||
# Make sure the user exists in Windows
|
||||
# Salt default is 'root'
|
||||
if not __salt__['user.info'](user):
|
||||
# User not found, use the account salt is running under
|
||||
# If username not found, use System
|
||||
user = __salt__['user.current']()
|
||||
if not user:
|
||||
user = 'SYSTEM'
|
||||
|
||||
if group is not None:
|
||||
log.warning(
|
||||
'The group argument for {0} has been ignored as this '
|
||||
|
|
|
@ -81,6 +81,9 @@ def user_exists(name, password=None, htpasswd_file=None, options='',
|
|||
ret['comment'] = useradd_ret['stderr']
|
||||
return ret
|
||||
|
||||
ret['result'] = True
|
||||
if __opts__['test'] and ret['changes']:
|
||||
ret['result'] = None
|
||||
else:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'User already known'
|
||||
return ret
|
||||
|
|
|
@ -23,6 +23,9 @@ A state module to manage LVMs
|
|||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import os
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
||||
|
@ -122,16 +125,17 @@ def vg_present(name, devices=None, **kwargs):
|
|||
if __salt__['lvm.vgdisplay'](name):
|
||||
ret['comment'] = 'Volume Group {0} already present'.format(name)
|
||||
for device in devices.split(','):
|
||||
pvs = __salt__['lvm.pvdisplay'](device)
|
||||
if pvs and pvs.get(device, None):
|
||||
if pvs[device]['Volume Group Name'] == name:
|
||||
realdev = os.path.realpath(device)
|
||||
pvs = __salt__['lvm.pvdisplay'](realdev)
|
||||
if pvs and pvs.get(realdev, None):
|
||||
if pvs[realdev]['Volume Group Name'] == name:
|
||||
ret['comment'] = '{0}\n{1}'.format(
|
||||
ret['comment'],
|
||||
'{0} is part of Volume Group'.format(device))
|
||||
elif pvs[device]['Volume Group Name'] == '#orphans_lvm2':
|
||||
__salt__['lvm.vgextend'](name, device)
|
||||
pvs = __salt__['lvm.pvdisplay'](device)
|
||||
if pvs[device]['Volume Group Name'] == name:
|
||||
elif pvs[realdev]['Volume Group Name'] == '#orphans_lvm2':
|
||||
__salt__['lvm.vgextend'](name, realdev)
|
||||
pvs = __salt__['lvm.pvdisplay'](realdev)
|
||||
if pvs[realdev]['Volume Group Name'] == name:
|
||||
ret['changes'].update(
|
||||
{device: 'added to {0}'.format(name)})
|
||||
else:
|
||||
|
@ -143,7 +147,7 @@ def vg_present(name, devices=None, **kwargs):
|
|||
ret['comment'] = '{0}\n{1}'.format(
|
||||
ret['comment'],
|
||||
'{0} is part of {1}'.format(
|
||||
device, pvs[device]['Volume Group Name']))
|
||||
device, pvs[realdev]['Volume Group Name']))
|
||||
ret['result'] = False
|
||||
else:
|
||||
ret['comment'] = '{0}\n{1}'.format(
|
||||
|
|
|
@ -34,6 +34,28 @@ these states. Here is some example SLS:
|
|||
- name: logstash
|
||||
- refresh: True
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
pkgrepo.managed:
|
||||
- humanname: deb-multimedia
|
||||
- name: deb http://www.deb-multimedia.org stable main
|
||||
- file: /etc/apt/sources.list.d/deb-multimedia.list
|
||||
- key_url: salt://deb-multimedia/files/marillat.pub
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
pkgrepo.managed:
|
||||
- humanname: Google Chrome
|
||||
- name: deb http://dl.google.com/linux/chrome/deb/ stable main
|
||||
- dist: stable
|
||||
- file: /etc/apt/sources.list.d/chrome-browser.list
|
||||
- require_in:
|
||||
- pkg: google-chrome-stable
|
||||
- gpgcheck: 1
|
||||
- key_url: https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base:
|
||||
|
@ -183,8 +205,11 @@ def managed(name, **kwargs):
|
|||
|
||||
key_url
|
||||
URL to retrieve a GPG key from. Allows the usage of ``http://``,
|
||||
``https://`` as well as ``salt://``. Use either ``keyid``/``keyserver``
|
||||
or ``key_url``, but not both.
|
||||
``https://`` as well as ``salt://``.
|
||||
|
||||
.. note::
|
||||
|
||||
Use either ``keyid``/``keyserver`` or ``key_url``, but not both.
|
||||
|
||||
consolidate
|
||||
If set to true, this will consolidate all sources definitions to
|
||||
|
|
|
@ -175,10 +175,9 @@ def _changes(name,
|
|||
change['homephone'] = homephone
|
||||
# OpenBSD login class
|
||||
if __grains__['kernel'] == 'OpenBSD':
|
||||
if not loginclass:
|
||||
loginclass = '""'
|
||||
if __salt__['user.get_loginclass'](name)['loginclass'] != loginclass:
|
||||
change['loginclass'] = loginclass
|
||||
if loginclass:
|
||||
if __salt__['user.get_loginclass'](name) != loginclass:
|
||||
change['loginclass'] = loginclass
|
||||
|
||||
return change
|
||||
|
||||
|
@ -439,6 +438,8 @@ def present(name,
|
|||
ret['comment'] = ('The following user attributes are set to be '
|
||||
'changed:\n')
|
||||
for key, val in six.iteritems(changes):
|
||||
if key == 'password':
|
||||
val = 'XXX-REDACTED-XXX'
|
||||
ret['comment'] += '{0}: {1}\n'.format(key, val)
|
||||
return ret
|
||||
# The user is present
|
||||
|
@ -513,8 +514,8 @@ def present(name,
|
|||
if lshad[key] != spost[key]:
|
||||
ret['changes'][key] = spost[key]
|
||||
if __grains__['kernel'] == 'OpenBSD':
|
||||
if lcpost['loginclass'] != lcpre['loginclass']:
|
||||
ret['changes']['loginclass'] = lcpost['loginclass']
|
||||
if lcpre != lcpost:
|
||||
ret['changes']['loginclass'] = lcpost
|
||||
if ret['changes']:
|
||||
ret['comment'] = 'Updated user {0}'.format(name)
|
||||
changes = _changes(name,
|
||||
|
@ -601,9 +602,9 @@ def present(name,
|
|||
if spost['passwd'] != password:
|
||||
ret['comment'] = 'User {0} created but failed to set' \
|
||||
' password to' \
|
||||
' {1}'.format(name, password)
|
||||
' {1}'.format(name, 'XXX-REDACTED-XXX')
|
||||
ret['result'] = False
|
||||
ret['changes']['password'] = password
|
||||
ret['changes']['password'] = 'XXX-REDACTED-XXX'
|
||||
if date:
|
||||
__salt__['shadow.set_date'](name, date)
|
||||
spost = __salt__['shadow.info'](name)
|
||||
|
@ -660,7 +661,12 @@ def present(name,
|
|||
ret['changes']['expire'] = expire
|
||||
elif salt.utils.is_windows():
|
||||
if password and not empty_password:
|
||||
ret['changes']['passwd'] = password
|
||||
if not __salt__['user.setpassword'](name, password):
|
||||
ret['comment'] = 'User {0} created but failed to set' \
|
||||
' password to' \
|
||||
' {1}'.format(name, 'XXX-REDACTED-XXX')
|
||||
ret['result'] = False
|
||||
ret['changes']['passwd'] = 'XXX-REDACTED-XXX'
|
||||
else:
|
||||
ret['comment'] = 'Failed to create new user {0}'.format(name)
|
||||
ret['result'] = False
|
||||
|
|
|
@ -18,6 +18,7 @@ DEVICE={{name}}
|
|||
{%endif%}{% if ipv6gateway %}IPV6_DEFAULTGW={{ipv6gateway}}
|
||||
{%endif%}{%endif%}{% if srcaddr %}SRCADDR={{srcaddr}}
|
||||
{%endif%}{% if peerdns %}PEERDNS={{peerdns}}
|
||||
{%endif%}{% if peerntp %}PEERNTP={{peerntp}}
|
||||
{%endif%}{% if bridge %}BRIDGE={{bridge}}
|
||||
{%endif%}{% if stp %}STP={{stp}}
|
||||
{%endif%}{% if delay or delay == 0 %}DELAY={{delay}}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
{%endif%}{% if ipv6gateway %}IPV6_DEFAULTGW="{{ipv6gateway}}"
|
||||
{%endif%}{%endif%}{% if srcaddr %}SRCADDR="{{srcaddr}}"
|
||||
{%endif%}{% if peerdns %}PEERDNS="{{peerdns}}"
|
||||
{%endif%}{% if peerntp %}PEERNTP="{{peerntp}}"
|
||||
{%endif%}{% if defroute %}DEFROUTE="{{defroute}}"
|
||||
{%endif%}{% if bridge %}BRIDGE="{{bridge}}"
|
||||
{%endif%}{% if stp %}STP="{{stp}}"
|
||||
|
|
|
@ -18,6 +18,7 @@ DEVICE="{{name}}"
|
|||
{%endif%}{% if ipv6gateway %}IPV6_DEFAULTGW="{{ipv6gateway}}"
|
||||
{%endif%}{%endif%}{% if srcaddr %}SRCADDR="{{srcaddr}}"
|
||||
{%endif%}{% if peerdns %}PEERDNS="{{peerdns}}"
|
||||
{%endif%}{% if peerntp %}PEERNTP="{{peerntp}}"
|
||||
{%endif%}{% if defroute %}DEFROUTE="{{defroute}}"
|
||||
{%endif%}{% if bridge %}BRIDGE="{{bridge}}"
|
||||
{%endif%}{% if stp %}STP="{{stp}}"
|
||||
|
|
|
@ -561,7 +561,6 @@ def get_ca_bundle(opts=None):
|
|||
|
||||
# Check Salt first
|
||||
for salt_root in file_roots.get('base', []):
|
||||
log.debug('file_roots is {0}'.format(salt_root))
|
||||
for path in ('cacert.pem', 'ca-bundle.crt'):
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
|
|
@ -13,6 +13,7 @@ import time
|
|||
import pprint
|
||||
from salt.ext.six.moves import range
|
||||
import salt.ext.six as six
|
||||
import salt.utils
|
||||
try:
|
||||
import requests
|
||||
HAS_REQUESTS = True # pylint: disable=W0612
|
||||
|
@ -68,6 +69,11 @@ def get_iam_region(version='latest', url='http://169.254.169.254',
|
|||
'''
|
||||
Gets instance identity document and returns region
|
||||
'''
|
||||
salt.utils.warn_until(
|
||||
'Carbon',
|
||||
'''The \'get_iam_region\' function has been deprecated in favor of
|
||||
\'salt.utils.aws.get_region_from_metadata\'. Please update your code
|
||||
to reflect this.''')
|
||||
instance_identity_url = '{0}/{1}/dynamic/instance-identity/document'.format(url, version)
|
||||
|
||||
region = None
|
||||
|
@ -85,6 +91,10 @@ def get_iam_metadata(version='latest', url='http://169.254.169.254',
|
|||
'''
|
||||
Grabs the first IAM role from this instances metadata if it exists.
|
||||
'''
|
||||
salt.utils.warn_until(
|
||||
'Carbon',
|
||||
'''The \'get_iam_metadata\' function has been deprecated in favor of
|
||||
\'salt.utils.aws.creds\'. Please update your code to reflect this.''')
|
||||
iam_url = '{0}/{1}/meta-data/iam/security-credentials/'.format(url, version)
|
||||
roles = _retry_get_url(iam_url, num_retries, timeout).splitlines()
|
||||
|
||||
|
|
88
tests/eventlisten.sh
Normal file
88
tests/eventlisten.sh
Normal file
|
@ -0,0 +1,88 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# The following example monitors Salt's event bus in a background process
|
||||
# watching for returns for a given job. Requires a POSIX environment and jq
|
||||
# <http://stedolan.github.io/jq/>.
|
||||
#
|
||||
# Usage: ./eventlisten.sh '*' test.sleep 10
|
||||
|
||||
# Mimic fnmatch from the Python stdlib.
|
||||
fnmatch() { case "$2" in $1) return 0 ;; *) return 1 ;; esac ; }
|
||||
count() { printf '%s\n' "$#" ; }
|
||||
|
||||
listen() {
|
||||
events='events'
|
||||
mkfifo $events
|
||||
exec 3<>$events # Hold the fd open.
|
||||
|
||||
# Start listening to events before starting the command to avoid race
|
||||
# conditions.
|
||||
salt-run state.event count=-1 >&3 &
|
||||
events_pid=$!
|
||||
|
||||
(
|
||||
timeout=$(( 60 * 60 ))
|
||||
sleep $timeout
|
||||
kill -s USR2 $$
|
||||
) &
|
||||
timeout_pid=$!
|
||||
|
||||
# Give the runner a few to connect to the event bus.
|
||||
printf 'Subscribing to the Salt event bus...\n'
|
||||
sleep 4
|
||||
|
||||
trap '
|
||||
excode=$?; trap - EXIT;
|
||||
exec 3>&-
|
||||
kill '"${timeout_pid}"'
|
||||
kill '"${events_pid}"'
|
||||
rm '"${events}"'
|
||||
exit
|
||||
echo $excode
|
||||
' INT TERM EXIT
|
||||
|
||||
trap '
|
||||
printf '\''Timeout reached; exiting.\n'\''
|
||||
exit 4
|
||||
' USR2
|
||||
|
||||
# Run the command and get the JID.
|
||||
jid=$(salt --async "$@")
|
||||
jid="${jid#*: }" # Remove leading text up to the colon.
|
||||
|
||||
# Create the event tags to listen for.
|
||||
start_tag="salt/job/${jid}/new"
|
||||
ret_tag="salt/job/${jid}/ret/*"
|
||||
|
||||
# ``read`` will block when no events are going through the bus.
|
||||
printf 'Waiting for tag %s\n' "$ret_tag"
|
||||
while read -r tag data; do
|
||||
if fnmatch "$start_tag" "$tag"; then
|
||||
minions=$(printf '%s\n' "${data}" | jq -r '.["minions"][]')
|
||||
num_minions=$(count $minions)
|
||||
printf 'Waiting for %s minions.\n' "$num_minions"
|
||||
continue
|
||||
fi
|
||||
|
||||
if fnmatch "$ret_tag" "$tag"; then
|
||||
mid="${tag##*/}"
|
||||
printf 'Got return for %s.\n' "$mid"
|
||||
printf 'Pretty-printing event: %s\n' "$tag"
|
||||
printf '%s\n' "$data" | jq .
|
||||
|
||||
minions="$(printf '%s\n' "$minions" | sed -e '/'"$mid"'/d')"
|
||||
num_minions=$(count $minions)
|
||||
if [ $((num_minions)) -eq 0 ]; then
|
||||
printf 'All minions returned.\n'
|
||||
break
|
||||
else
|
||||
printf 'Remaining minions: %s\n' "$num_minions"
|
||||
fi
|
||||
else
|
||||
printf 'Skipping tag: %s\n' "$tag"
|
||||
continue
|
||||
fi
|
||||
done <&3
|
||||
}
|
||||
|
||||
listen "$@"
|
|
@ -31,6 +31,7 @@ def __random_name(size=6):
|
|||
|
||||
# Create the cloud instance name to be used throughout the tests
|
||||
INSTANCE_NAME = __random_name()
|
||||
PROVIDER_NAME = 'joyent'
|
||||
|
||||
|
||||
class JoyentTest(integration.ShellCase):
|
||||
|
@ -46,57 +47,57 @@ class JoyentTest(integration.ShellCase):
|
|||
super(JoyentTest, self).setUp()
|
||||
|
||||
# check if appropriate cloud provider and profile files are present
|
||||
profile_str = 'joyent-config:'
|
||||
provider = 'joyent'
|
||||
profile_str = 'joyent-config'
|
||||
providers = self.run_cloud('--list-providers')
|
||||
if profile_str not in providers:
|
||||
if profile_str + ':' not in providers:
|
||||
self.skipTest(
|
||||
'Configuration file for {0} was not found. Check {0}.conf files '
|
||||
'in tests/integration/files/conf/cloud.*.d/ to run these tests.'
|
||||
.format(provider)
|
||||
.format(PROVIDER_NAME)
|
||||
)
|
||||
|
||||
# check if user, password, private_key, and keyname are present
|
||||
path = os.path.join(integration.FILES,
|
||||
'conf',
|
||||
'cloud.providers.d',
|
||||
provider + '.conf')
|
||||
config = cloud_providers_config(path)
|
||||
config = cloud_providers_config(
|
||||
os.path.join(
|
||||
integration.FILES,
|
||||
'conf',
|
||||
'cloud.providers.d',
|
||||
PROVIDER_NAME + '.conf'
|
||||
)
|
||||
)
|
||||
|
||||
user = config['joyent-config'][provider]['user']
|
||||
password = config['joyent-config'][provider]['password']
|
||||
private_key = config['joyent-config'][provider]['private_key']
|
||||
keyname = config['joyent-config'][provider]['keyname']
|
||||
user = config[profile_str][PROVIDER_NAME]['user']
|
||||
password = config[profile_str][PROVIDER_NAME]['password']
|
||||
private_key = config[profile_str][PROVIDER_NAME]['private_key']
|
||||
keyname = config[profile_str][PROVIDER_NAME]['keyname']
|
||||
|
||||
if user == '' or password == '' or private_key == '' or keyname == '':
|
||||
self.skipTest(
|
||||
'A user name, password, private_key file path, and a key name '
|
||||
'must be provided to run these tests. Check '
|
||||
'tests/integration/files/conf/cloud.providers.d/{0}.conf'
|
||||
.format(provider)
|
||||
.format(PROVIDER_NAME)
|
||||
)
|
||||
|
||||
def test_instance(self):
|
||||
'''
|
||||
Test creating and deleting instance on Joyent
|
||||
'''
|
||||
|
||||
# create the instance
|
||||
instance = self.run_cloud('-p joyent-test {0}'.format(INSTANCE_NAME))
|
||||
ret_str = ' {0}'.format(INSTANCE_NAME)
|
||||
|
||||
# check if instance with salt installed returned
|
||||
try:
|
||||
self.assertIn(ret_str, instance)
|
||||
self.assertIn(
|
||||
INSTANCE_NAME,
|
||||
[i.strip() for i in self.run_cloud('-p joyent-test {0}'.format(INSTANCE_NAME))]
|
||||
)
|
||||
except AssertionError:
|
||||
self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME))
|
||||
raise
|
||||
|
||||
# delete the instance
|
||||
delete = self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME))
|
||||
ret_str = ' True'
|
||||
try:
|
||||
self.assertIn(ret_str, delete)
|
||||
self.assertIn(
|
||||
INSTANCE_NAME + ':',
|
||||
[i.strip() for i in self.run_cloud('-d {0} --assume-yes'.format(INSTANCE_NAME))]
|
||||
)
|
||||
except AssertionError:
|
||||
raise
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class LocalemodTestCase(TestCase):
|
|||
Test for Get the current system locale
|
||||
'''
|
||||
with patch.dict(localemod.__grains__, {'os_family': ['Arch']}):
|
||||
with patch.object(localemod, '_localectl_get', return_value=True):
|
||||
with patch.object(localemod, '_locale_get', return_value=True):
|
||||
self.assertTrue(localemod.get_locale())
|
||||
|
||||
with patch.dict(localemod.__grains__, {'os_family': ['Gentoo']}):
|
||||
|
|
|
@ -22,8 +22,7 @@ ensure_in_syspath('../../')
|
|||
import salt.utils
|
||||
from salt.modules import timezone
|
||||
import os
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
|
||||
# Globals
|
||||
|
@ -53,22 +52,28 @@ class TimezoneTestCase(TestCase):
|
|||
with patch('salt.utils.fopen', mock_open(read_data=file_data),
|
||||
create=True) as mfile:
|
||||
mfile.return_value.__iter__.return_value = file_data.splitlines()
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Debian'}):
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Debian',
|
||||
'os': 'Debian'}):
|
||||
self.assertEqual(timezone.get_zone(), '#\nA')
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo'}):
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo',
|
||||
'os': 'Gentoo'}):
|
||||
self.assertEqual(timezone.get_zone(), '')
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'FreeBSD'}):
|
||||
with patch.object(os, 'readlink',
|
||||
return_value='/usr/share/zoneinfo/'):
|
||||
self.assertEqual(timezone.get_zone(), '')
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'FreeBSD',
|
||||
'os': 'FreeBSD'}):
|
||||
zone = 'America/Denver'
|
||||
linkpath = '/usr/share/zoneinfo/' + zone
|
||||
with patch.object(os, 'readlink', return_value=linkpath):
|
||||
self.assertEqual(timezone.get_zone(), zone)
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Solaris'}):
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value='A=B')}):
|
||||
self.assertEqual(timezone.get_zone(), 'B')
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Solaris',
|
||||
'os': 'Solaris'}):
|
||||
fl_data = 'TZ=Foo\n'
|
||||
with patch('salt.utils.fopen',
|
||||
mock_open(read_data=fl_data)) as mfile:
|
||||
mfile.return_value.__iter__.return_value = [fl_data]
|
||||
self.assertEqual(timezone.get_zone(), 'Foo')
|
||||
|
||||
def test_get_zonecode(self):
|
||||
'''
|
||||
|
@ -179,26 +184,24 @@ class TimezoneTestCase(TestCase):
|
|||
self.assertEqual(timezone.get_hwclock(), 'A')
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Debian'}):
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value='A=yes')}):
|
||||
fl_data = 'UTC=yes\n'
|
||||
with patch('salt.utils.fopen',
|
||||
mock_open(read_data=fl_data)) as mfile:
|
||||
mfile.return_value.__iter__.return_value = [fl_data]
|
||||
self.assertEqual(timezone.get_hwclock(), 'UTC')
|
||||
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value='A=no')}):
|
||||
fl_data = 'UTC=no\n'
|
||||
with patch('salt.utils.fopen',
|
||||
mock_open(read_data=fl_data)) as mfile:
|
||||
mfile.return_value.__iter__.return_value = [fl_data]
|
||||
self.assertEqual(timezone.get_hwclock(), 'localtime')
|
||||
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value='A')}):
|
||||
self.assertEqual(timezone.get_hwclock(), 'A')
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Gentoo'}):
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value='A=B')}):
|
||||
self.assertEqual(timezone.get_hwclock(), 'B')
|
||||
fl_data = 'clock=UTC\n'
|
||||
with patch('salt.utils.fopen',
|
||||
mock_open(read_data=fl_data)) as mfile:
|
||||
mfile.return_value.__iter__.return_value = [fl_data]
|
||||
self.assertEqual(timezone.get_hwclock(), 'UTC')
|
||||
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.object(os.path, 'isfile', mock):
|
||||
|
@ -234,31 +237,30 @@ class TimezoneTestCase(TestCase):
|
|||
'''
|
||||
Test to sets the hardware clock to be either UTC or localtime
|
||||
'''
|
||||
ret = ('UTC is the only choice for SPARC architecture')
|
||||
ret1 = ('Zone does not exist: /usr/share/zoneinfo/America/Denver')
|
||||
with patch.object(timezone, 'get_zone',
|
||||
return_value='America/Denver'):
|
||||
zone = 'America/Denver'
|
||||
with patch.object(timezone, 'get_zone', return_value=zone):
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Solaris',
|
||||
'cpuarch': 'sparc'}):
|
||||
self.assertEqual(timezone.set_hwclock('clock'), ret)
|
||||
self.assertRaises(
|
||||
SaltInvocationError,
|
||||
timezone.set_hwclock,
|
||||
'clock'
|
||||
)
|
||||
self.assertRaises(
|
||||
SaltInvocationError,
|
||||
timezone.set_hwclock,
|
||||
'localtime'
|
||||
)
|
||||
|
||||
with patch.dict(timezone.__salt__,
|
||||
{'cmd.run':
|
||||
MagicMock(return_value=None)}):
|
||||
self.assertTrue(timezone.set_hwclock('localtime'))
|
||||
|
||||
self.assertTrue(timezone.set_hwclock('UTC'))
|
||||
|
||||
with patch.dict(timezone.__grains__, {'os_family': 'Sola'}):
|
||||
with patch.dict(timezone.__grains__,
|
||||
{'os_family': 'DoesNotMatter'}):
|
||||
with patch.object(os.path, 'exists', return_value=False):
|
||||
self.assertEqual(timezone.set_hwclock('clock'), ret1)
|
||||
|
||||
with patch.object(os.path, 'exists', return_value=True):
|
||||
with patch.object(os, 'unlink', return_value=None):
|
||||
with patch.object(os, 'symlink', return_value=None):
|
||||
self.assertTrue(timezone.set_hwclock('clock'))
|
||||
|
||||
self.assertRaises(
|
||||
CommandExecutionError,
|
||||
timezone.set_hwclock,
|
||||
'UTC'
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
|
|
|
@ -347,33 +347,6 @@ class UserAddTestCase(TestCase):
|
|||
with patch.object(useradd, 'info', mock):
|
||||
self.assertFalse(useradd.chhomephone('salt', 1))
|
||||
|
||||
# 'chloginclass' function tests: 1
|
||||
|
||||
def test_chloginclass(self):
|
||||
'''
|
||||
Test if the default login class of the user is changed
|
||||
'''
|
||||
with patch.dict(useradd.__grains__, {'kernel': 'Linux'}):
|
||||
self.assertFalse(useradd.chloginclass('salt', 'staff'))
|
||||
|
||||
with patch.dict(useradd.__grains__, {'kernel': 'OpenBSD'}):
|
||||
mock_login = MagicMock(return_value={'loginclass': 'staff'})
|
||||
with patch.object(useradd, 'get_loginclass', mock_login):
|
||||
self.assertTrue(useradd.chloginclass('salt', 'staff'))
|
||||
|
||||
mock = MagicMock(return_value=None)
|
||||
with patch.dict(useradd.__salt__, {'cmd.run': mock}):
|
||||
mock = MagicMock(side_effect=[{'loginclass': '""'},
|
||||
{'loginclass': 'staff'}])
|
||||
with patch.object(useradd, 'get_loginclass', mock):
|
||||
self.assertTrue(useradd.chloginclass('salt', 'staff'))
|
||||
|
||||
mock_login = MagicMock(return_value={'loginclass': 'staff1'})
|
||||
with patch.object(useradd, 'get_loginclass', mock_login):
|
||||
mock = MagicMock(return_value=None)
|
||||
with patch.dict(useradd.__salt__, {'cmd.run': mock}):
|
||||
self.assertFalse(useradd.chloginclass('salt', 'staff'))
|
||||
|
||||
# 'info' function tests: 1
|
||||
|
||||
def test_info(self):
|
||||
|
@ -394,27 +367,6 @@ class UserAddTestCase(TestCase):
|
|||
with patch.object(useradd, 'list_groups', mock):
|
||||
self.assertEqual(useradd.info('salt')['name'], '_TEST_GROUP')
|
||||
|
||||
# 'get_loginclass' function tests: 1
|
||||
|
||||
def test_get_loginclass(self):
|
||||
'''
|
||||
Test the login class of the user
|
||||
'''
|
||||
with patch.dict(useradd.__grains__, {'kernel': 'Linux'}):
|
||||
self.assertFalse(useradd.get_loginclass('salt'))
|
||||
|
||||
with patch.dict(useradd.__grains__, {'kernel': 'OpenBSD'}):
|
||||
mock = MagicMock(return_value='class staff')
|
||||
with patch.dict(useradd.__salt__, {'cmd.run_stdout': mock}):
|
||||
self.assertDictEqual(useradd.get_loginclass('salt'),
|
||||
{'loginclass': 'staff'})
|
||||
|
||||
with patch.dict(useradd.__grains__, {'kernel': 'OpenBSD'}):
|
||||
mock = MagicMock(return_value='class ')
|
||||
with patch.dict(useradd.__salt__, {'cmd.run_stdout': mock}):
|
||||
self.assertDictEqual(useradd.get_loginclass('salt'),
|
||||
{'loginclass': '""'})
|
||||
|
||||
# 'list_groups' function tests: 1
|
||||
|
||||
@patch('salt.utils.get_group_list', MagicMock(return_value='Salt'))
|
||||
|
|
|
@ -69,7 +69,7 @@ class WinPathTestCase(TestCase):
|
|||
'''
|
||||
Test to Returns the system path
|
||||
'''
|
||||
mock = MagicMock(return_value='c:\\salt')
|
||||
mock = MagicMock(return_value={'vdata': 'c:\\salt'})
|
||||
with patch.dict(win_path.__salt__, {'reg.read_key': mock}):
|
||||
self.assertListEqual(win_path.get_path(), ['c:\\salt'])
|
||||
|
||||
|
@ -85,12 +85,12 @@ class WinPathTestCase(TestCase):
|
|||
'''
|
||||
Test to add the directory to the SYSTEM path
|
||||
'''
|
||||
mock = MagicMock(return_value=['c:\\salt'])
|
||||
with patch.object(win_path, 'get_path', mock):
|
||||
mock = MagicMock(return_value=True)
|
||||
with patch.dict(win_path.__salt__, {'reg.set_key': mock}):
|
||||
mock = MagicMock(side_effect=[True, False])
|
||||
with patch.object(win_path, 'rehash', mock):
|
||||
mock_get = MagicMock(return_value=['c:\\salt'])
|
||||
with patch.object(win_path, 'get_path', mock_get):
|
||||
mock_set = MagicMock(return_value=True)
|
||||
with patch.dict(win_path.__salt__, {'reg.set_value': mock_set}):
|
||||
mock_rehash = MagicMock(side_effect=[True, False])
|
||||
with patch.object(win_path, 'rehash', mock_rehash):
|
||||
self.assertTrue(win_path.add("c:\\salt", 1))
|
||||
|
||||
self.assertFalse(win_path.add("c:\\salt", 1))
|
||||
|
@ -99,14 +99,14 @@ class WinPathTestCase(TestCase):
|
|||
'''
|
||||
Test to remove the directory from the SYSTEM path
|
||||
'''
|
||||
mock = MagicMock(side_effect=[[1], ['c:\\salt'], ['c:\\salt']])
|
||||
with patch.object(win_path, 'get_path', mock):
|
||||
mock_get = MagicMock(side_effect=[[1], ['c:\\salt'], ['c:\\salt']])
|
||||
with patch.object(win_path, 'get_path', mock_get):
|
||||
self.assertTrue(win_path.remove("c:\\salt"))
|
||||
|
||||
mock = MagicMock(side_effect=[True, False])
|
||||
with patch.dict(win_path.__salt__, {'reg.set_key': mock}):
|
||||
mock = MagicMock(return_value="Salt")
|
||||
with patch.object(win_path, 'rehash', mock):
|
||||
mock_set = MagicMock(side_effect=[True, False])
|
||||
with patch.dict(win_path.__salt__, {'reg.set_value': mock_set}):
|
||||
mock_rehash = MagicMock(return_value="Salt")
|
||||
with patch.object(win_path, 'rehash', mock_rehash):
|
||||
self.assertEqual(win_path.remove("c:\\salt"), "Salt")
|
||||
|
||||
self.assertFalse(win_path.remove("c:\\salt"))
|
||||
|
|
Loading…
Add table
Reference in a new issue