mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
Merge pull request #27198 from basepi/merge-forward-develop
Merge forward from 2015.8 to develop
This commit is contained in:
commit
9ddc88c6ba
@ -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:
|
||||
|
22
doc/_themes/saltstack2/layout.html
vendored
22
doc/_themes/saltstack2/layout.html
vendored
@ -21,7 +21,7 @@
|
||||
|
||||
{% set script_files = [
|
||||
'_static/js/core.min.js',
|
||||
'_static/js/webhelp.min_v1.4.1.js',
|
||||
'_static/js/webhelp.min_v1.4.2.js',
|
||||
] %}
|
||||
|
||||
{% set css_files = [
|
||||
@ -160,6 +160,12 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if build_type == "inactive" and on_saltstack %}
|
||||
<div id="dev-notification">
|
||||
<div class="alert alert-warning dev-notification-text releaselinks" role="alert"><i class="glyphicon glyphicon-cog"></i> You are viewing docs from a branch that is no longer active. You might want to view docs for the <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the latest stable release" href="/en/latest/">{{ latest_release }}</a> release instead.</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{%- block document %}
|
||||
<div class="body-content">
|
||||
{% block body %} {% endblock %}
|
||||
@ -192,8 +198,11 @@
|
||||
{% elif build_type == "previous" %}
|
||||
<p>You are viewing docs for the previous stable release, {{ previous_release }}. Switch to docs for the latest stable release, <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the latest stable release" href="/en/latest/">{{ latest_release }}</a>, or to a recent doc build from the <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Latest docs from the develop branch" href="/en/develop/">develop</a> branch.</p>
|
||||
|
||||
{% elif build_type == "inactive" %}
|
||||
<p>You are viewing docs for an inactive release, {{ previous_release }}. Switch to docs for the latest stable release, <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the latest stable release" href="/en/latest/">{{ latest_release }}</a>, or to a recent doc build from the <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Latest docs from the develop branch" href="/en/develop/">develop</a> branch.</p>
|
||||
|
||||
{% elif build_type == "develop" %}
|
||||
<p>You are viewing docs built from a recent snapshot of the develop branch. Switch to docs for the latest stable release, <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the latest stable release" href="/en/latest/">{{ latest_release }}</a>, or to docs for the previous stable release, <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the previous stable release" href="/en/{{ previous_release_dir }}/">{{ previous_release }}</a>.</p>
|
||||
<p>You are viewing docs built from a recent snapshot of the develop branch. Switch to docs for the latest stable release, <a data-container="body" data-toggle="tooltip" data-placement="bottom" title="Docs for the latest stable release" href="/en/latest/">{{ latest_release }}</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
@ -203,12 +212,9 @@
|
||||
|
||||
<div class="col-sm-6">
|
||||
|
||||
<a href="http://saltstack.com/saltstack-enterprise-4-0-now-with-gui/" target="_blank"><img class="nolightbox nav-banner" src="{{ pathto('_static/images/saltStack_enterprise_350x125.jpg', 1) }}"/></a>
|
||||
<a href="http://saltstack.com/events/" target="_blank"><img class="nolightbox nav-banner center" src="{{ pathto('_static/images/saltStack_events_300x300.jpg', 1) }}"/></a>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<p><b>SaltStack Training</b></p>
|
||||
<p><a href="http://www.saltstack.com/training/">Now offering remote attendee training!</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -224,9 +230,9 @@
|
||||
<a class="ss-logo" href="http://saltstack.com"><img width="250" height="63" class="nolightbox center" src="{{ pathto('_static/images/saltstack_logo.svg', 1) }}"></a>
|
||||
|
||||
{% if on_saltstack %}
|
||||
<div class="versions {{ build_type }}">
|
||||
<div class="releaselinks versions {{ build_type }}">
|
||||
|
||||
<a class="btn btn-secondary{% if build_type == "previous" %} active{% endif %}" id="previous"{% if build_type == "previous" %} title="View release notes"{% else %} title="Switch to docs for the previous stable release"{% endif %} data-container="body" data-toggle="tooltip" data-placement="bottom" href="/en/{{ previous_release_dir }}/">{{ previous_release }}{% if build_type == "previous" %} <i class="glyphicon glyphicon-ok"></i>{%- endif %}</a>
|
||||
<a class="btn btn-secondary{% if build_type == "previous" or build_type == "inactive" %} active{% endif %}" id="previous"{% if build_type == "previous" or build_type == "inactive" %} title="View release notes"{% else %} title="Switch to docs for the previous stable release"{% endif %} data-container="body" data-toggle="tooltip" data-placement="bottom" href="/en/{{ previous_release_dir }}/">{{ previous_release }}{% if build_type == "previous" or build_type == "inactive" %} <i class="glyphicon glyphicon-ok"></i>{%- endif %}</a>
|
||||
|
||||
<a class="btn btn-secondary{% if build_type == "latest" %} active{% endif %}" id="latest"{% if build_type == "latest" %} title="View release notes"{% else %} title="Switch to docs for the latest stable release"{% endif %} data-container="body" data-toggle="tooltip" data-placement="bottom" href="/en/latest/">{{ latest_release }}{% if build_type == "latest" %} <i class="glyphicon glyphicon-ok"></i>{% endif %}</a>
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
BIN
doc/_themes/saltstack2/static/images/saltStack_events_300x300.jpg
vendored
Normal file
BIN
doc/_themes/saltstack2/static/images/saltStack_events_300x300.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
@ -115,7 +115,7 @@ $( document ).ready(function() {
|
||||
});
|
||||
|
||||
/*version page selector*/
|
||||
$( 'div.versions' ).on('click', 'a', function (e) {
|
||||
$( 'div.releaselinks' ).on('click', 'a', function (e) {
|
||||
e.preventDefault();
|
||||
var clickedVer = $(this).attr("href");
|
||||
var $currentVer = $( 'div.versions' ).find( 'a.active' ).first();
|
||||
@ -188,4 +188,4 @@ function resizeend() {
|
||||
|
||||
function last(list) {
|
||||
return list[list.length - 1];
|
||||
}
|
||||
}
|
23
doc/conf.py
23
doc/conf.py
@ -50,6 +50,7 @@ MOCK_MODULES = [
|
||||
'Crypto.PublicKey',
|
||||
'Crypto.Random',
|
||||
'Crypto.Signature',
|
||||
'Crypto.Signature.PKCS1_v1_5',
|
||||
'M2Crypto',
|
||||
'msgpack',
|
||||
'yaml',
|
||||
@ -87,6 +88,7 @@ MOCK_MODULES = [
|
||||
'tornado.httpserver',
|
||||
'tornado.httputil',
|
||||
'tornado.ioloop',
|
||||
'tornado.simple_httpclient',
|
||||
'tornado.web',
|
||||
'tornado.websocket',
|
||||
|
||||
@ -104,7 +106,6 @@ MOCK_MODULES = [
|
||||
'MySQLdb.cursors',
|
||||
'nagios_json',
|
||||
'psutil',
|
||||
'psutil.version_info',
|
||||
'pycassa',
|
||||
'pymongo',
|
||||
'rabbitmq_server',
|
||||
@ -162,9 +163,9 @@ project = 'Salt'
|
||||
copyright = '2015 SaltStack, Inc.'
|
||||
|
||||
version = salt.version.__version__
|
||||
latest_release = '2015.5.5' # latest release
|
||||
previous_release = '2014.7.6' # latest release from previous branch
|
||||
previous_release_dir = '2014.7' # path on web server for previous branch
|
||||
latest_release = '2015.8.0' # latest release
|
||||
previous_release = '2015.5.5' # latest release from previous branch
|
||||
previous_release_dir = '2015.5' # path on web server for previous branch
|
||||
build_type = 'develop' # latest, previous, develop
|
||||
|
||||
# set release to 'version' for develop so sha is used
|
||||
@ -176,11 +177,11 @@ release = latest_release # version, latest_release, previous_release
|
||||
# Set google custom search engine
|
||||
|
||||
if release == latest_release:
|
||||
search_cx = '004624818632696854117:yfmprrbw3pk'
|
||||
search_cx = '004624818632696854117:yfmprrbw3pk' # latest
|
||||
elif release.startswith('2014.7'):
|
||||
search_cx = '004624818632696854117:thhslradbru'
|
||||
search_cx = '004624818632696854117:thhslradbru' # 2014.7
|
||||
elif release.startswith('2015.5'):
|
||||
search_cx = '004624818632696854117:ovogwef29do'
|
||||
search_cx = '004624818632696854117:ovogwef29do' # 2015.5
|
||||
else:
|
||||
search_cx = '004624818632696854117:haj7bjntf4s' # develop
|
||||
|
||||
@ -228,11 +229,11 @@ rst_prolog = """\
|
||||
.. _`salt-packagers`: https://groups.google.com/forum/#!forum/salt-packagers
|
||||
.. |windownload| raw:: html
|
||||
|
||||
<p>x86: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-x86-Setup.exe"><strong>Salt-Minion-{release}-x86-Setup.exe</strong></a>
|
||||
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-x86-Setup.exe.md5"><strong>md5</strong></a></p>
|
||||
<p>x86: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-3-x86-Setup.exe"><strong>Salt-Minion-{release}-3-x86-Setup.exe</strong></a>
|
||||
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-3-x86-Setup.exe.md5"><strong>md5</strong></a></p>
|
||||
|
||||
<p>AMD64: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-AMD64-Setup.exe"><strong>Salt-Minion-{release}-AMD64-Setup.exe</strong></a>
|
||||
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-AMD64-Setup.exe.md5"><strong>md5</strong></a></p>
|
||||
<p>AMD64: <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-3-AMD64-Setup.exe"><strong>Salt-Minion-{release}-3-AMD64-Setup.exe</strong></a>
|
||||
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-3-AMD64-Setup.exe.md5"><strong>md5</strong></a></p>
|
||||
|
||||
""".format(release=release)
|
||||
|
||||
|
@ -247,6 +247,7 @@ Glossary
|
||||
whether or not the module should be available to a minion. This
|
||||
function commonly contains logic to determine if all requirements
|
||||
for a module are available, such as external libraries.
|
||||
|
||||
Worker
|
||||
A master process which can send notices and receive replies from
|
||||
minions. *See also*:
|
||||
|
@ -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,
|
||||
|
@ -1,6 +0,0 @@
|
||||
==================================
|
||||
salt.cloud.clouds.digital_ocean_v2
|
||||
==================================
|
||||
|
||||
.. automodule:: salt.cloud.clouds.digital_ocean_v2
|
||||
:members:
|
@ -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__)
|
||||
|
@ -1,6 +0,0 @@
|
||||
===============
|
||||
salt.states.apt
|
||||
===============
|
||||
|
||||
.. automodule:: salt.states.apt
|
||||
:members:
|
@ -4,6 +4,8 @@
|
||||
Requisites and Other Global State Arguments
|
||||
===========================================
|
||||
|
||||
.. _requisites-fire-event:
|
||||
|
||||
Fire Event Notifications
|
||||
========================
|
||||
|
||||
|
@ -239,6 +239,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
|
||||
----------
|
||||
@ -140,7 +140,34 @@ instance.
|
||||
|
||||
disk_size
|
||||
---------
|
||||
The amount of disk space that will be allocated to this image, in megabytes.
|
||||
The amount of disk space that will be allocated to this image, in gigabytes.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
base_softlayer_ubuntu:
|
||||
disk_size: 100
|
||||
|
||||
Using Multiple Disks
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 2015.8.1
|
||||
|
||||
SoftLayer allows up to 5 disks to be specified for a virtual machine upon
|
||||
creation. Multiple disks can be specified either as a list or a comma-delimited
|
||||
string. The first ``disk_size`` specified in the string or list will be the first
|
||||
disk size assigned to the VM.
|
||||
|
||||
List Example:
|
||||
.. code-block:: yaml
|
||||
|
||||
base_softlayer_ubuntu:
|
||||
disk_size: ['100', '20', '20']
|
||||
|
||||
String Example:
|
||||
.. code-block:: yaml
|
||||
|
||||
base_softlayer_ubuntu:
|
||||
disk_size: '100, 20, 20'
|
||||
|
||||
local_disk
|
||||
----------
|
||||
|
@ -1,14 +1,46 @@
|
||||
.. _installation-debian:
|
||||
|
||||
===================
|
||||
Debian Installation
|
||||
===================
|
||||
|
||||
Currently the latest packages for Debian Old Stable, Stable, and
|
||||
Unstable (Squeeze, Wheezy, and Sid) are published in our
|
||||
(saltstack.com) Debian repository.
|
||||
.. _installation-debian-repo:
|
||||
|
||||
Installation from the SaltStack Repository
|
||||
==========================================
|
||||
|
||||
Debian 8 packages are available in the SaltStack Debian repository.
|
||||
|
||||
To install using the SaltStack Debian repository:
|
||||
|
||||
#. Run the following command to import the SaltStack repository key:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget -O - https://repo.saltstack.com/apt/debian/SALTSTACK-GPG-KEY.pub | sudo apt-key add -
|
||||
|
||||
#. Add the following line to ``/etc/apt/sources.list``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
deb http://repo.saltstack.com/apt/debian jessie contrib
|
||||
|
||||
#. Run ``sudo apt-get update``
|
||||
|
||||
#. Install the salt-minion, salt-master, or other Salt components:
|
||||
|
||||
- ``apt-get install salt-master``
|
||||
- ``apt-get install salt-minion``
|
||||
- ``apt-get install salt-ssh``
|
||||
- ``apt-get install salt-syndic``
|
||||
- ``apt-get install salt-cloud``
|
||||
|
||||
Configure Apt
|
||||
-------------
|
||||
|
||||
Currently the latest packages for Debian Old Stable, Stable, and
|
||||
Unstable (Squeeze, Wheezy, and Sid) are published in our
|
||||
(saltstack.com) Debian repository.
|
||||
|
||||
Squeeze (Old Old Stable)
|
||||
------------------------
|
||||
|
@ -1,26 +1,98 @@
|
||||
.. _installation-rhel:
|
||||
|
||||
==============================================================
|
||||
RHEL / CentOS / Scientific Linux / Amazon Linux / Oracle Linux
|
||||
==============================================================
|
||||
|
||||
.. _installation-rhel-repo:
|
||||
|
||||
Installation from the SaltStack Repository
|
||||
==========================================
|
||||
|
||||
To install using the SaltStack yum repository:
|
||||
|
||||
#. Run one of the following commands based on your version to import the SaltStack repository key:
|
||||
|
||||
Version 7:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget https://repo.saltstack.com/yum/rhel7/SALTSTACK-GPG-KEY.pub
|
||||
rpm --import SALTSTACK-GPG-KEY.pub
|
||||
rm -f SALTSTACK-GPG-KEY.pub
|
||||
|
||||
Version 6:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget https://repo.saltstack.com/yum/rhel6/SALTSTACK-GPG-KEY.pub
|
||||
rpm --import SALTSTACK-GPG-KEY.pub
|
||||
rm -f SALTSTACK-GPG-KEY.pub
|
||||
|
||||
Version 5:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
wget https://repo.saltstack.com/yum/rhel5/SALTSTACK-EL5-GPG-KEY.pub
|
||||
rpm --import SALTSTACK-EL5-GPG-KEY.pub
|
||||
rm -f SALTSTACK-EL5-GPG-KEY.pub
|
||||
|
||||
#. Save the following file to ``/etc/yum.repos.d/saltstack.repo``:
|
||||
|
||||
Versions 6 / 7:
|
||||
|
||||
.. code-block:: config
|
||||
|
||||
####################
|
||||
# Enable SaltStack's package repository
|
||||
[saltstack-repo]
|
||||
name=SaltStack repo for RHEL/CentOS $releasever
|
||||
baseurl=https://repo.saltstack.com/yum/rhel$releasever
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://repo.saltstack.com/yum/rhel$releasever/SALTSTACK-GPG-KEY.pub
|
||||
|
||||
Version 5:
|
||||
|
||||
.. code-block:: config
|
||||
|
||||
####################
|
||||
# Enable SaltStack's package repository
|
||||
[saltstack-repo]
|
||||
name=SaltStack repo for RHEL/CentOS $releasever
|
||||
baseurl=https://repo.saltstack.com/yum/rhel$releasever
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://repo.saltstack.com/yum/rhel$releasever/SALTSTACK-EL5-GPG-KEY.pub
|
||||
|
||||
#. Run ``sudo yum clean expire-cache``
|
||||
|
||||
#. Run ``sudo yum update``
|
||||
|
||||
#. Install the salt-minion, salt-master, or other Salt components:
|
||||
|
||||
- ``yum install salt-master``
|
||||
- ``yum install salt-minion``
|
||||
- ``yum install salt-ssh``
|
||||
- ``yum install salt-syndic``
|
||||
- ``yum install salt-cloud``
|
||||
|
||||
.. note::
|
||||
EPEL support is not required when installing using the SaltStack repository
|
||||
on Red Hat 6 and 7. EPEL must be enabled when installing on Red Hat 5.
|
||||
|
||||
Installation from Repository
|
||||
============================
|
||||
|
||||
.. _installation-rhel-5:
|
||||
|
||||
RHEL/CentOS 5
|
||||
-------------
|
||||
|
||||
Due to the removal of some of Salt's dependencies from EPEL5, we have created a
|
||||
repository on `Fedora COPR`_. Moving forward, this will be the official means
|
||||
of installing Salt on RHEL5-based systems. Information on how to enable this
|
||||
repository can be found here__.
|
||||
|
||||
.. _`Fedora COPR`: https://copr.fedoraproject.org/
|
||||
.. __: https://copr.fedoraproject.org/coprs/saltstack/salt-el5/
|
||||
|
||||
RHEL/CentOS 6 and 7, Scientific Linux, etc.
|
||||
-------------------------------------------
|
||||
|
||||
.. warning::
|
||||
Salt 2015.8 requires ``python-crypto`` 2.6.1 or higher, and ``python-tornado`` version
|
||||
4.2.1 or higher. These packages are not currently available in EPEL for
|
||||
Red Hat 5 and 6. You must install these dependencies from another location
|
||||
or use the SaltStack repository documented above.
|
||||
|
||||
Beginning with version 0.9.4, Salt has been available in `EPEL`_. It is
|
||||
installable using yum. Salt should work properly with all mainstream
|
||||
derivatives of RHEL, including CentOS, Scientific Linux, Oracle Linux and
|
||||
@ -34,6 +106,16 @@ installing salt on RHEL6.
|
||||
|
||||
.. _`EPEL`: http://fedoraproject.org/wiki/EPEL
|
||||
|
||||
.. _installation-rhel-5:
|
||||
|
||||
RHEL/CentOS 5
|
||||
-------------
|
||||
|
||||
Due to the removal of some of Salt's dependencies from EPEL5, we recommend
|
||||
using the :ref:`SaltStack Repository <installation-rhel-repo>` or
|
||||
the repository on `Fedora COPR`_.
|
||||
|
||||
.. _`Fedora COPR`: https://copr.fedoraproject.org/coprs/saltstack/salt-el5/
|
||||
|
||||
Enabling EPEL
|
||||
*************
|
||||
@ -119,12 +201,10 @@ ZeroMQ 4
|
||||
========
|
||||
|
||||
We recommend using ZeroMQ 4 where available. SaltStack provides ZeroMQ 4.0.4
|
||||
and pyzmq 14.3.1 in a COPR_ repository. Instructions for adding this repository
|
||||
(as well as for upgrading ZeroMQ and pyzmq on existing minions) can be found
|
||||
here__.
|
||||
and pyzmq 14.3.1 in the :ref:`SaltStack Repository <installation-rhel-repo>`
|
||||
as well as a COPR_ repository.
|
||||
|
||||
.. _COPR: http://copr.fedoraproject.org/
|
||||
.. __: http://copr.fedoraproject.org/coprs/saltstack/zeromq4/
|
||||
.. _COPR: http://copr.fedoraproject.org/coprs/saltstack/zeromq4/
|
||||
|
||||
If this repo is added *before* Salt is installed, then installing either
|
||||
``salt-master`` or ``salt-minion`` will automatically pull in ZeroMQ 4.0.4, and
|
||||
@ -136,7 +216,6 @@ additional states to upgrade ZeroMQ and pyzmq are unnecessary.
|
||||
because YUM will not be able to process the SHA256 checksums used by COPR.
|
||||
|
||||
.. note::
|
||||
|
||||
For RHEL/CentOS 5 installations, if using the new repository to install
|
||||
Salt (as detailed :ref:`above <installation-rhel-5>`), then it is not
|
||||
necessary to enable the zeromq4 COPR, as the new EL5 repository includes
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _windows:
|
||||
|
||||
=======
|
||||
Windows
|
||||
=======
|
||||
@ -11,6 +13,7 @@ to control your Salt Minions on Windows.
|
||||
Many of the standard Salt modules have been ported to work on Windows and many
|
||||
of the Salt States currently work on Windows, as well.
|
||||
|
||||
.. _windows-installer:
|
||||
|
||||
Windows Installer
|
||||
=================
|
||||
@ -46,9 +49,10 @@ Windows service.
|
||||
If the minion won't start, try installing the Microsoft Visual C++ 2008 x64 SP1
|
||||
redistributable. Allow all Windows updates to run salt-minion smoothly.
|
||||
|
||||
.. _windows-installer-options:
|
||||
|
||||
Silent Installer option
|
||||
=======================
|
||||
Silent Installer Options
|
||||
========================
|
||||
|
||||
The installer can be run silently by providing the `/S` option at the command
|
||||
line. The options `/master` and `/minion-name` allow for configuring the master
|
||||
|
@ -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:
|
||||
|
@ -10,12 +10,23 @@ usual the release notes are not exhaustive and primarily include the most
|
||||
notable additions and improvements. Hundreds of bugs have been fixed and many
|
||||
modules have been substantially updated and added.
|
||||
|
||||
New SaltStack Installation Repositories
|
||||
=======================================
|
||||
|
||||
SaltStack now provides installation repositories for several platforms, with more to come.
|
||||
See the following links for instructions:
|
||||
|
||||
- :ref:`Red Hat / CentOS 5, 6, 7 <installation-rhel-repo>`
|
||||
- :ref:`Debian 8 <installation-debian-repo>`
|
||||
- :ref:`Windows <windows-installer>`
|
||||
- FreeBSD
|
||||
|
||||
Send Event on State Completion
|
||||
==============================
|
||||
|
||||
A `fire_event` global state keyword argument was added that allows any state to
|
||||
send an event upon completion. Useful for custom progress bars and checking in
|
||||
on long state runs.
|
||||
on long state runs. See :ref:`fire_event <requisites-fire-event>`.
|
||||
|
||||
ZeroMQ socket monitoring
|
||||
========================
|
||||
@ -35,7 +46,7 @@ A new :conf_master:`default_top` option was added to load the state top file
|
||||
from a single, specific environment, rather than merging top data across all
|
||||
environments. Additionally, new :conf_master:`top_file_merge_strategy` and
|
||||
:conf_master:`env_order` options were added for more control over top file
|
||||
merging.
|
||||
merging. See :ref:`The Top File <states-top>`.
|
||||
|
||||
Tornado TCP Transport
|
||||
=====================
|
||||
@ -47,12 +58,12 @@ performance.
|
||||
|
||||
.. note::
|
||||
|
||||
Tornado is considered early-access in this release. The following known
|
||||
Tornado is considered expiremental in this release. The following known
|
||||
issues were being investigated at the time of release:
|
||||
- TCP tests show
|
||||
performance degredation over time (:issue:`26051`)
|
||||
- TCP transport stacktrace on windows minion: Future exception was never
|
||||
retrieved (:issue:`25718)
|
||||
retrieved (:issue:`25718`)
|
||||
- [freebsd] TCP transport not working in 2015.8.0rc3 (:issue:`26364`)
|
||||
|
||||
Proxy Minion Enhancements
|
||||
@ -80,19 +91,16 @@ with :mod:`gitfs <salt.fileserver.gitfs>`. Support for pygit2_ has been added,
|
||||
bringing with it the ability to access authenticated repositories.
|
||||
|
||||
Using the new features will require updates to the git ext_pillar
|
||||
configuration, further details can be found :ref:`here
|
||||
<git-pillar-2015-8-0-and-later>`.
|
||||
configuration, further details can be found in the :ref:`pillar.git_pillar
|
||||
<git-pillar-2015-8-0-and-later>` docs.
|
||||
|
||||
Salt Cloud Improvements
|
||||
=======================
|
||||
|
||||
- Pricing data from several cloud providers (GCE, DigitalOcean, SoftLayer_HW, EC2)
|
||||
- All cloud providers now use standardized bootstrapping code
|
||||
- All cloud providers now use standardized bootstrapping code.
|
||||
- Modified the Linode Salt Cloud driver to use Linode's native API instead of
|
||||
depending on apache-libcloud or linode-python.
|
||||
- When querying for VMs in ``digital_ocean.py``, the number of VMs to include in
|
||||
a page was changed from 20 (default) to 200 to reduce the number of API calls
|
||||
to Digital
|
||||
|
||||
Salt Cloud Changes
|
||||
------------------
|
||||
@ -103,9 +111,7 @@ Salt Cloud Changes
|
||||
duplicate names of VMs before trying to create a new VM. Will now throw an
|
||||
error similarly to other salt-cloud drivers when trying to create a VM of the
|
||||
same name, even if the VM is in the ``terminated`` state.
|
||||
- Modified the Linode Salt Cloud driver to use Linode's native API instead of
|
||||
depending on apache-libcloud or linode-python.
|
||||
- When querying for VMs in ``ditigal_ocean.py``, the number of VMs to include in
|
||||
- When querying for VMs in ``digital_ocean.py``, the number of VMs to include in
|
||||
a page was changed from 20 (default) to 200 to reduce the number of API calls
|
||||
to Digital Ocean.Ocean.
|
||||
|
||||
@ -125,7 +131,7 @@ State and Execution Module Improvements
|
||||
- Deprecate ``enabled`` argument in ``pkgrepo.managed`` in favor of ``disabled``.
|
||||
- Archive module changes: In the ``archive.tar`` and ``archive.cmd_unzip``
|
||||
module functions, remove the arbitrary prefixing of the options string with
|
||||
``-``. An options string beginning with a ``--long-option``, would have
|
||||
``-``. An options string beginning with a ``--long-option``, would have
|
||||
uncharacteristically needed its first ``-`` removed under the former scheme.
|
||||
Also, tar will parse its options differently if short options are used with or
|
||||
without a preceding ``-``, so it is better to not confuse the user into
|
||||
@ -138,10 +144,12 @@ State and Execution Module Improvements
|
||||
Windows Improvements
|
||||
====================
|
||||
|
||||
- Templatize Windows Software package definitions
|
||||
- Added additional capabilities to the user module for windows
|
||||
- Added new module for managing windows updates (win_wua)
|
||||
- Turned on multi-processing by default for windows in minion conf
|
||||
- Enhanced the windows minion silent installation with command line parameters
|
||||
to configure the salt master and minion name. See :ref:`Silent Installer
|
||||
Options <windows-installer-options>`.
|
||||
- Improved user management with additional capabilities in the user module for Windows.
|
||||
- Improved patch management with a new module for managing windows updates (:mod:`win_wua <modules.win_wua>`).
|
||||
- Turned on multi-processing by default for windows in minion configuration.
|
||||
|
||||
Windows Software Repo Changes
|
||||
-----------------------------
|
||||
@ -190,7 +198,6 @@ seconds by specifying ``in_seconds=True``.
|
||||
Other Improvements
|
||||
==================
|
||||
|
||||
- Python 3
|
||||
- Sanitize sensitive fields in http.query
|
||||
- Allow authorization to be read from Django and eauth
|
||||
- Add templating to SMTP returner
|
||||
@ -201,6 +208,7 @@ Other Improvements
|
||||
- Add end time to master job cache for jobs (optional, off by default)
|
||||
- Tornado is now the default backend for http.request
|
||||
- Support pillarenv selection as it's done for saltenv
|
||||
- salt was updated to use python-crypto version 2.6.1, which removes the dependency on python-m2crypto.
|
||||
|
||||
Deprecations
|
||||
============
|
||||
@ -282,3 +290,4 @@ Major Bug Fixes
|
||||
- Improve process management in proxy minion (:issue:`12024`)
|
||||
- Proxy minion never comes up with message ' I am XXX and I am not supposed to
|
||||
start any proxies.' (:issue:`25908`)
|
||||
- Fixed an issue that caused an exception when using Salt mine from pillar. (:issue:`11509`)
|
||||
|
@ -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>`
|
||||
|
@ -5,16 +5,16 @@ Provide authentication using Stormpath.
|
||||
This driver requires some extra configuration beyond that which Stormpath
|
||||
normally requires.
|
||||
|
||||
.. code-block:: yaml
|
||||
.. code-block:: yaml
|
||||
|
||||
stormpath:
|
||||
apiid: 1234567890
|
||||
apikey: 1234567890/ABCDEF
|
||||
# Can use an application ID
|
||||
application: 6789012345
|
||||
# Or can use a directory ID
|
||||
directory: 3456789012
|
||||
# But not both
|
||||
stormpath:
|
||||
apiid: 1234567890
|
||||
apikey: 1234567890/ABCDEF
|
||||
# Can use an application ID
|
||||
application: 6789012345
|
||||
# Or can use a directory ID
|
||||
directory: 3456789012
|
||||
# But not both
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
'''
|
||||
|
@ -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']))
|
||||
|
@ -137,7 +137,7 @@ def avail_images(call=None):
|
||||
for image in items['images']:
|
||||
ret[image['id']] = {}
|
||||
for item in six.iterkeys(image):
|
||||
ret[image['id']][item] = str(image[item])
|
||||
ret[image['id']][item] = image[item]
|
||||
|
||||
page += 1
|
||||
try:
|
||||
@ -184,10 +184,36 @@ def list_nodes(call=None):
|
||||
while fetch:
|
||||
items = query(method='droplets', command='?page=' + str(page) + '&per_page=200')
|
||||
for node in items['droplets']:
|
||||
networks = node['networks']
|
||||
v4s = networks.get('v4')
|
||||
v6s = networks.get('v6')
|
||||
public_ips = []
|
||||
private_ips = []
|
||||
|
||||
if v4s:
|
||||
for item in v4s:
|
||||
ip_type = item.get('type')
|
||||
ip_address = item.get('ip_address')
|
||||
if ip_type == 'public':
|
||||
public_ips.append(ip_address)
|
||||
if ip_type == 'private':
|
||||
private_ips.append(ip_address)
|
||||
|
||||
if v6s:
|
||||
for item in v6s:
|
||||
ip_type = item.get('type')
|
||||
ip_address = item.get('ip_address')
|
||||
if ip_type == 'public':
|
||||
public_ips.append(ip_address)
|
||||
if ip_type == 'private':
|
||||
private_ips.append(ip_address)
|
||||
|
||||
ret[node['name']] = {
|
||||
'id': node['id'],
|
||||
'image': node['image']['name'],
|
||||
'networks': str(node['networks']),
|
||||
'name': node['name'],
|
||||
'private_ips': private_ips,
|
||||
'public_ips': public_ips,
|
||||
'size': node['size_slug'],
|
||||
'state': str(node['status']),
|
||||
}
|
||||
@ -244,9 +270,9 @@ def get_image(vm_):
|
||||
Return the image object to use
|
||||
'''
|
||||
images = avail_images()
|
||||
vm_image = str(config.get_cloud_config_value(
|
||||
vm_image = config.get_cloud_config_value(
|
||||
'image', vm_, __opts__, search_global=False
|
||||
))
|
||||
)
|
||||
for image in images:
|
||||
if vm_image in (images[image]['name'],
|
||||
images[image]['slug'],
|
||||
|
@ -3188,6 +3188,7 @@ def list_nodes(call=None):
|
||||
ret[node] = {
|
||||
'id': nodes[node]['id'],
|
||||
'image': nodes[node]['image'],
|
||||
'name': nodes[node]['name'],
|
||||
'size': nodes[node]['size'],
|
||||
'state': nodes[node]['state'],
|
||||
'private_ips': nodes[node]['private_ips'],
|
||||
|
@ -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]$')
|
||||
|
||||
|
@ -222,7 +222,7 @@ def list_nodes(conn=None, call=None):
|
||||
nodes = list_nodes_full(conn, call)
|
||||
for node in nodes:
|
||||
ret[node] = {}
|
||||
for prop in 'id', 'image', 'size', 'state', 'private_ips', 'public_ips':
|
||||
for prop in 'id', 'image', 'name', 'size', 'state', 'private_ips', 'public_ips':
|
||||
ret[node][prop] = nodes[node][prop]
|
||||
return ret
|
||||
|
||||
|
@ -275,16 +275,43 @@ def create(vm_):
|
||||
'domain': vm_['domain'],
|
||||
'startCpus': vm_['cpu_number'],
|
||||
'maxMemory': vm_['ram'],
|
||||
'localDiskFlag': vm_['local_disk'],
|
||||
'hourlyBillingFlag': vm_['hourly_billing'],
|
||||
}
|
||||
|
||||
local_disk_flag = config.get_cloud_config_value(
|
||||
'local_disk', vm_, __opts__, default=False
|
||||
)
|
||||
kwargs['localDiskFlag'] = local_disk_flag
|
||||
|
||||
if 'image' in vm_:
|
||||
kwargs['operatingSystemReferenceCode'] = vm_['image']
|
||||
kwargs['blockDevices'] = [{
|
||||
'device': '0',
|
||||
'diskImage': {'capacity': vm_['disk_size']},
|
||||
}]
|
||||
kwargs['blockDevices'] = []
|
||||
disks = vm_['disk_size']
|
||||
|
||||
if isinstance(disks, int):
|
||||
disks = [str(disks)]
|
||||
elif isinstance(disks, str):
|
||||
disks = [size.strip() for size in disks.split(',')]
|
||||
|
||||
count = 0
|
||||
for disk in disks:
|
||||
# device number '1' is reserved for the SWAP disk
|
||||
if count == 1:
|
||||
count += 1
|
||||
block_device = {'device': str(count),
|
||||
'diskImage': {'capacity': str(disk)}}
|
||||
kwargs['blockDevices'].append(block_device)
|
||||
count += 1
|
||||
|
||||
# Upper bound must be 5 as we're skipping '1' for the SWAP disk ID
|
||||
if count > 5:
|
||||
log.warning('More that 5 disks were specified for {0} .'
|
||||
'The first 5 disks will be applied to the VM, '
|
||||
'but the remaining disks will be ignored.\n'
|
||||
'Please adjust your cloud configuration to only '
|
||||
'specify a maximum of 5 disks.'.format(vm_['name']))
|
||||
break
|
||||
|
||||
elif 'global_identifier' in vm_:
|
||||
kwargs['blockDeviceTemplateGroup'] = {
|
||||
'globalIdentifier': vm_['global_identifier']
|
||||
|
@ -436,6 +436,7 @@ def list_nodes(conn=None, call=None):
|
||||
ret[node.name] = {
|
||||
'id': node.id,
|
||||
'image': node.image,
|
||||
'name': node.name,
|
||||
'private_ips': node.private_ips,
|
||||
'public_ips': node.public_ips,
|
||||
'size': node.size,
|
||||
|
@ -32,6 +32,7 @@ from salt.ext.six.moves.urllib.parse import urlparse
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.dictupdate
|
||||
import salt.utils.network
|
||||
import salt.syspaths
|
||||
import salt.utils.validate.path
|
||||
@ -1481,7 +1482,7 @@ def include_config(include, orig_path, verbose):
|
||||
|
||||
for fn_ in sorted(glob.glob(path)):
|
||||
log.debug('Including configuration from \'{0}\''.format(fn_))
|
||||
configuration.update(_read_conf_file(fn_))
|
||||
salt.utils.dictupdate.update(configuration, _read_conf_file(fn_))
|
||||
return configuration
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -6,7 +6,6 @@ from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import contextlib
|
||||
import errno
|
||||
import logging
|
||||
import hashlib
|
||||
import os
|
||||
@ -23,6 +22,7 @@ import salt.payload
|
||||
import salt.transport
|
||||
import salt.fileserver
|
||||
import salt.utils
|
||||
import salt.utils.files
|
||||
import salt.utils.templates
|
||||
import salt.utils.url
|
||||
import salt.utils.gzip_util
|
||||
@ -512,7 +512,8 @@ class Client(object):
|
||||
ret.sort()
|
||||
return ret
|
||||
|
||||
def get_url(self, url, dest, makedirs=False, saltenv='base', env=None, no_cache=False):
|
||||
def get_url(self, url, dest, makedirs=False, saltenv='base',
|
||||
env=None, no_cache=False):
|
||||
'''
|
||||
Get a single file from a URL.
|
||||
'''
|
||||
@ -617,27 +618,59 @@ class Client(object):
|
||||
password=url_data.password,
|
||||
**get_kwargs
|
||||
)
|
||||
|
||||
if 'handle' not in query:
|
||||
raise MinionError('Error: {0}'.format(query['error']))
|
||||
|
||||
try:
|
||||
content_length = int(query['handle'].headers['Content-Length'])
|
||||
except (AttributeError, KeyError, ValueError):
|
||||
# Shouldn't happen but don't let this raise an exception.
|
||||
# Instead, just don't do content length checking below.
|
||||
log.warning(
|
||||
'No Content-Length header in HTTP response from fetch of '
|
||||
'{0}, or Content-Length is non-numeric'.format(fixed_url)
|
||||
)
|
||||
content_length = None
|
||||
|
||||
if no_cache:
|
||||
return ''.join(result)
|
||||
content = ''.join(result)
|
||||
if content_length is not None \
|
||||
and len(content) > content_length:
|
||||
return content[-content_length:]
|
||||
else:
|
||||
return content
|
||||
else:
|
||||
destfp.close()
|
||||
destfp = None
|
||||
# Can't just do an os.rename() here, this results in a
|
||||
# WindowsError being raised when the destination path exists on
|
||||
# a Windows machine. Have to remove the file.
|
||||
try:
|
||||
os.remove(dest)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise MinionError(
|
||||
'Error: Unable to remove {0}: {1}'.format(
|
||||
dest,
|
||||
exc.strerror
|
||||
)
|
||||
dest_tmp_size = os.path.getsize(dest_tmp)
|
||||
if content_length is not None \
|
||||
and dest_tmp_size > content_length:
|
||||
log.warning(
|
||||
'Size of file downloaded from {0} ({1}) does not '
|
||||
'match the Content-Length ({2}). This is probably due '
|
||||
'to an upstream bug in tornado '
|
||||
'(https://github.com/tornadoweb/tornado/issues/1518). '
|
||||
'Re-writing the file to correct this.'.format(
|
||||
fixed_url,
|
||||
dest_tmp_size,
|
||||
content_length
|
||||
)
|
||||
os.rename(dest_tmp, dest)
|
||||
)
|
||||
dest_tmp_bak = dest_tmp + '.bak'
|
||||
salt.utils.files.rename(dest_tmp, dest_tmp_bak)
|
||||
with salt.utils.fopen(dest_tmp_bak, 'rb') as fp_bak:
|
||||
fp_bak.seek(dest_tmp_size - content_length)
|
||||
with salt.utils.fopen(dest_tmp, 'wb') as fp_new:
|
||||
while True:
|
||||
chunk = fp_bak.read(
|
||||
self.opts['file_buffer_size']
|
||||
)
|
||||
if not chunk:
|
||||
break
|
||||
fp_new.write(chunk)
|
||||
os.remove(dest_tmp_bak)
|
||||
salt.utils.files.rename(dest_tmp, dest)
|
||||
return dest
|
||||
except HTTPError as exc:
|
||||
raise MinionError('HTTP error {0} reading {1}: {3}'.format(
|
||||
|
@ -1738,7 +1738,7 @@ def _hw_data(osdata):
|
||||
for serial in ('system-serial-number', 'chassis-serial-number', 'baseboard-serial-number'):
|
||||
serial = __salt__['smbios.get'](serial)
|
||||
if serial is not None:
|
||||
grains['serial'] = serial
|
||||
grains['serialnumber'] = serial
|
||||
break
|
||||
elif osdata['kernel'] == 'FreeBSD':
|
||||
# On FreeBSD /bin/kenv (already in base system)
|
||||
|
@ -1213,8 +1213,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):
|
||||
'''
|
||||
|
@ -74,7 +74,7 @@ def __virtual__():
|
||||
'''
|
||||
if not HAS_BOTO:
|
||||
return False
|
||||
__utils__['boto.assign_funcs'](__name__, 'dynamodb')
|
||||
__utils__['boto.assign_funcs'](__name__, 'dynamodb2')
|
||||
return True
|
||||
|
||||
|
||||
|
@ -3342,6 +3342,7 @@ def worktree_prune(cwd,
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
.. _`git-worktree(1)`: http://git-scm.com/docs/git-worktree
|
||||
.. _`git-config(1)`: http://git-scm.com/docs/git-config/2.5.1
|
||||
|
||||
|
||||
CLI Examples:
|
||||
|
@ -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,12 +122,12 @@ 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']:
|
||||
if salt.utils.which('localectl'):
|
||||
return _localectl_get()
|
||||
return _locale_get()
|
||||
|
||||
cmd = 'grep "^LANG=" /etc/default/locale'
|
||||
elif 'Gentoo' in __grains__['os_family']:
|
||||
@ -236,10 +259,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'):
|
||||
@ -267,7 +292,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__)
|
||||
|
||||
|
@ -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 = [
|
||||
|
@ -161,6 +161,13 @@ def _sync(form, saltenv=None):
|
||||
mod_file = os.path.join(__opts__['cachedir'], 'module_refresh')
|
||||
with salt.utils.fopen(mod_file, 'a+') as ofile:
|
||||
ofile.write('')
|
||||
if form == 'grains' and \
|
||||
__opts__.get('grains_cache') and \
|
||||
os.path.isfile(os.path.join(__opts__['cachedir'], 'grains.cache.p')):
|
||||
try:
|
||||
os.remove(os.path.join(__opts__['cachedir'], 'grains.cache.p'))
|
||||
except OSError:
|
||||
log.error('Could not remove grains cache!')
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
||||
@ -206,7 +264,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:
|
||||
@ -219,39 +277,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):
|
||||
@ -267,33 +350,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
|
||||
|
@ -152,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'
|
||||
)
|
||||
|
||||
@ -194,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__)
|
||||
|
||||
@ -677,7 +678,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
|
||||
|
@ -764,13 +764,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
|
||||
|
@ -131,24 +131,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
|
||||
@ -159,19 +158,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.
|
||||
|
@ -128,7 +128,7 @@ def ext_pillar(minion_id,
|
||||
prefix='',
|
||||
service_url=None,
|
||||
kms_keyid=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
|
||||
|
||||
'''
|
||||
@ -382,6 +382,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
|
||||
|
||||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
@ -79,12 +80,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:
|
||||
@ -97,18 +102,25 @@ 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_:
|
||||
if six.PY2:
|
||||
fn_.write(jid)
|
||||
else:
|
||||
fn_.write(bytes(jid, 'utf-8'))
|
||||
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_:
|
||||
if six.PY2:
|
||||
fn_.write(jid)
|
||||
else:
|
||||
fn_.write(bytes(jid, 'utf-8'))
|
||||
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
|
||||
|
||||
|
@ -816,6 +816,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
|
||||
|
@ -170,10 +170,9 @@ def _changes(name,
|
||||
change['homephone'] = homephone
|
||||
# OpenBSD/FreeBSD login class
|
||||
if __grains__['kernel'] in ('OpenBSD', 'FreeBSD'):
|
||||
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
|
||||
|
||||
@ -434,6 +433,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
|
||||
@ -506,8 +507,8 @@ def present(name,
|
||||
for key in spost:
|
||||
if lshad[key] != spost[key]:
|
||||
ret['changes'][key] = spost[key]
|
||||
if __grains__['kernel'] in ('OpenBSD', 'FreeBSD') and lcpost['loginclass'] != lcpre['loginclass']:
|
||||
ret['changes']['loginclass'] = lcpost['loginclass']
|
||||
if __grains__['kernel'] in ('OpenBSD', 'FreeBSD') and lcpost != lcpre:
|
||||
ret['changes']['loginclass'] = lcpost
|
||||
if ret['changes']:
|
||||
ret['comment'] = 'Updated user {0}'.format(name)
|
||||
changes = _changes(name,
|
||||
@ -594,9 +595,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)
|
||||
@ -652,7 +653,12 @@ def present(name,
|
||||
ret['result'] = False
|
||||
ret['changes']['expire'] = expire
|
||||
elif salt.utils.is_windows() and 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}}"
|
||||
|
@ -27,6 +27,7 @@ DEVICE="{{name}}"
|
||||
{%endif%}{%endif%}{% if srcaddr %}SRCADDR="{{srcaddr}}"
|
||||
{%endif%}{% if peerdns %}PEERDNS="{{peerdns}}"
|
||||
{%endif%}{% if peerroutes %}PEERROUTES="{{peerroutes}}"
|
||||
{%endif%}{% if peerntp %}PEERNTP="{{peerntp}}"
|
||||
{%endif%}{% if defroute %}DEFROUTE="{{defroute}}"
|
||||
{%endif%}{% if bridge %}BRIDGE="{{bridge}}"
|
||||
{%endif%}{% if stp %}STP="{{stp}}"
|
||||
|
@ -3,6 +3,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Python libs
|
||||
import errno
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
@ -10,7 +11,7 @@ import subprocess
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.modules.selinux
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.exceptions import CommandExecutionError, MinionError
|
||||
|
||||
|
||||
def recursive_copy(source, dest):
|
||||
@ -87,3 +88,19 @@ def copyfile(source, dest, backup_mode='', cachedir=''):
|
||||
os.remove(tgt)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def rename(src, dst):
|
||||
'''
|
||||
On Windows, os.rename() will fail with a WindowsError exception if a file
|
||||
exists at the destination path. This function removes the destination path
|
||||
first before attempting the os.rename().
|
||||
'''
|
||||
try:
|
||||
os.remove(dst)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.ENOENT:
|
||||
raise MinionError(
|
||||
'Error: Unable to remove {0}: {1}'.format(dst, exc.strerror)
|
||||
)
|
||||
os.rename(src, dst)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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']}):
|
||||
@ -59,16 +59,6 @@ class LocalemodTestCase(TestCase):
|
||||
MagicMock(return_value='A')}):
|
||||
self.assertEqual(localemod.get_locale(), '')
|
||||
|
||||
@patch('salt.utils.which', MagicMock(side_effect=['/usr/bin/localectl', None]))
|
||||
def test_get_locale_debian(self):
|
||||
with patch.dict(localemod.__grains__, {'os_family': ['Debian']}):
|
||||
with patch.object(localemod, '_localectl_get', return_value=True):
|
||||
self.assertTrue(localemod.get_locale())
|
||||
|
||||
with patch.dict(localemod.__salt__, {'cmd.run':
|
||||
MagicMock(return_value='LC_ALL=C')}):
|
||||
self.assertEqual(localemod.get_locale(), 'C')
|
||||
|
||||
def test_set_locale(self):
|
||||
'''
|
||||
Test for Sets the current system locale
|
||||
|
@ -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):
|
||||
'''
|
||||
@ -183,26 +188,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):
|
||||
@ -238,31 +241,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…
Reference in New Issue
Block a user