Merge branch '2016.3' into 'develop'
Conflicts: - conf/master - doc/ref/configuration/master.rst - salt/cli/batch.py - salt/cli/daemons.py - salt/config/__init__.py - salt/minion.py - salt/modules/aptpkg.py - salt/modules/beacons.py - salt/states/archive.py - salt/states/cmd.py - salt/utils/gitfs.py - tests/unit/states/cmd_test.py
This commit is contained in:
Коммит
cbf42a8407
|
@ -65,6 +65,9 @@ tags
|
|||
# Allow a user to set their own _version.py for testing
|
||||
_version.py
|
||||
|
||||
# Ignore auto generated _syspaths.py file
|
||||
_syspaths.py
|
||||
|
||||
# Ignore grains file written out during tests
|
||||
tests/integration/files/conf/grains
|
||||
/salt/_syspaths.py
|
||||
|
|
33
conf/master
33
conf/master
|
@ -591,7 +591,7 @@
|
|||
# and the first repo to have the file will return it.
|
||||
# When using the git backend branches and tags are translated into salt
|
||||
# environments.
|
||||
# Note: file:// repos will be treated as a remote, so refs you want used must
|
||||
# Note: file:// repos will be treated as a remote, so refs you want used must
|
||||
# exist in that repo as *local* refs.
|
||||
#gitfs_remotes:
|
||||
# - git://github.com/saltstack/salt-states.git
|
||||
|
@ -723,19 +723,20 @@
|
|||
|
||||
# A master can cache pillars locally to bypass the expense of having to render them
|
||||
# for each minion on every request. This feature should only be enabled in cases
|
||||
# where pillar rendering time is known to be unsatisfactory and any attendent security
|
||||
# where pillar rendering time is known to be unsatisfactory and any attendant security
|
||||
# concerns about storing pillars in a master cache have been addressed.
|
||||
#
|
||||
# When enabling this feature, be certain to read through the additional pillar_cache_*
|
||||
# configuration options to fully understand the tuneable parameters and their implications.
|
||||
# When enabling this feature, be certain to read through the additional ``pillar_cache_*``
|
||||
# configuration options to fully understand the tunable parameters and their implications.
|
||||
#
|
||||
# Note: setting ``pillar_cache: True`` has no effect on targeting Minions with Pillars.
|
||||
# See https://docs.saltstack.com/en/latest/topics/targeting/pillar.html
|
||||
#pillar_cache: False
|
||||
|
||||
# If and only if a master has set `pillar_cache: True`, the cache TTL controls the amount
|
||||
# If and only if a master has set ``pillar_cache: True``, the cache TTL controls the amount
|
||||
# of time, in seconds, before the cache is considered invalid by a master and a fresh
|
||||
# pillar is recompiled and stored.
|
||||
#
|
||||
# pillar_cache_ttl: 3600
|
||||
#pillar_cache_ttl: 3600
|
||||
|
||||
# If and only if a master has set `pillar_cache: True`, one of several storage providers
|
||||
# can be utililzed.
|
||||
|
@ -745,20 +746,18 @@
|
|||
# Note that pillars are stored UNENCRYPTED. Ensure that the master cache
|
||||
# has permissions set appropriately. (Same defaults are provided.)
|
||||
#
|
||||
#`memory`: [EXPERIMENTAL] An optional backend for pillar caches which uses a pure-Python
|
||||
# in-memory data structure for maximal performance. There are several cavaets,
|
||||
# however. First, because each master worker contains its own in-memory cache,
|
||||
# there is no guarantee of cache consistency between minion requests. This
|
||||
# works best in situations where the pillar rarely if ever changes. Secondly,
|
||||
# and perhaps more importantly, this means that unencrypted pillars will
|
||||
# be accessible to any process which can examine the memory of the salt-master!
|
||||
# This may represent a substantial security risk.
|
||||
# memory: [EXPERIMENTAL] An optional backend for pillar caches which uses a pure-Python
|
||||
# in-memory data structure for maximal performance. There are several caveats,
|
||||
# however. First, because each master worker contains its own in-memory cache,
|
||||
# there is no guarantee of cache consistency between minion requests. This
|
||||
# works best in situations where the pillar rarely if ever changes. Secondly,
|
||||
# and perhaps more importantly, this means that unencrypted pillars will
|
||||
# be accessible to any process which can examine the memory of the ``salt-master``!
|
||||
# This may represent a substantial security risk.
|
||||
#
|
||||
#pillar_cache_backend: disk
|
||||
|
||||
|
||||
|
||||
|
||||
##### Syndic settings #####
|
||||
##########################################
|
||||
# The Salt syndic is used to pass commands through a master from a higher
|
||||
|
|
|
@ -89,7 +89,7 @@ Execution Options
|
|||
|
||||
.. option:: -u, --update-bootstrap
|
||||
|
||||
Update salt-bootstrap to the latest develop version on GitHub.
|
||||
Update salt-bootstrap to the latest stable bootstrap release.
|
||||
|
||||
.. option:: -y, --assume-yes
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ of the Salt system each have a respective configuration file. The
|
|||
:command:`salt-minion` is configured via the minion configuration file.
|
||||
|
||||
.. seealso::
|
||||
:ref:`example master configuration file <configuration-examples-master>`
|
||||
:ref:`Example master configuration file <configuration-examples-master>`.
|
||||
|
||||
The configuration file for the salt-master is located at
|
||||
:file:`/etc/salt/master` by default. A notable exception is FreeBSD, where the
|
||||
|
@ -20,7 +20,6 @@ options are as follows:
|
|||
Primary Master Configuration
|
||||
============================
|
||||
|
||||
|
||||
.. conf_master:: interface
|
||||
|
||||
``interface``
|
||||
|
@ -1610,7 +1609,6 @@ authenticate is protected by a passphrase.
|
|||
|
||||
gitfs_passphrase: mypassphrase
|
||||
|
||||
|
||||
hg: Mercurial Remote File Server Backend
|
||||
----------------------------------------
|
||||
|
||||
|
@ -2135,13 +2133,13 @@ configuration is the same as :conf_master:`file_roots`:
|
|||
prod:
|
||||
- /srv/pillar/prod
|
||||
|
||||
.. _master-configuration-ext-pillar:
|
||||
|
||||
.. conf_master:: ext_pillar
|
||||
|
||||
``ext_pillar``
|
||||
--------------
|
||||
|
||||
.. _master-configuration-ext-pillar:
|
||||
|
||||
The ext_pillar option allows for any number of external pillar interfaces to be
|
||||
called when populating pillar data. The configuration is based on ext_pillar
|
||||
functions. The available ext_pillar functions can be found herein:
|
||||
|
@ -2198,7 +2196,7 @@ pillar_roots_override_ext_pillar option and will be removed in future releases.
|
|||
|
||||
ext_pillar_first: False
|
||||
|
||||
.. _git_pillar-config-opts:
|
||||
.. _git-pillar-config-opts:
|
||||
|
||||
Git External Pillar (git_pillar) Configuration Options
|
||||
------------------------------------------------------
|
||||
|
@ -2392,6 +2390,7 @@ they were created by a different master.
|
|||
|
||||
.. __: http://www.gluster.org/
|
||||
|
||||
.. _git-ext-pillar-auth-opts:
|
||||
|
||||
Git External Pillar Authentication Options
|
||||
******************************************
|
||||
|
@ -2499,10 +2498,15 @@ authenticate is protected by a passphrase.
|
|||
|
||||
git_pillar_passphrase: mypassphrase
|
||||
|
||||
.. _pillar-merging-opts:
|
||||
|
||||
Pillar Merging Options
|
||||
----------------------
|
||||
|
||||
.. conf_master:: pillar_source_merging_strategy
|
||||
|
||||
``pillar_source_merging_strategy``
|
||||
----------------------------------
|
||||
**********************************
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
|
@ -2511,7 +2515,7 @@ Default: ``smart``
|
|||
The pillar_source_merging_strategy option allows you to configure merging
|
||||
strategy between different sources. It accepts 4 values:
|
||||
|
||||
* recurse:
|
||||
* ``recurse``:
|
||||
|
||||
it will merge recursively mapping of data. For example, theses 2 sources:
|
||||
|
||||
|
@ -2537,7 +2541,7 @@ strategy between different sources. It accepts 4 values:
|
|||
element2: True
|
||||
baz: quux
|
||||
|
||||
* aggregate:
|
||||
* ``aggregate``:
|
||||
|
||||
instructs aggregation of elements between sources that use the #!yamlex renderer.
|
||||
|
||||
|
@ -2572,7 +2576,7 @@ strategy between different sources. It accepts 4 values:
|
|||
- quux
|
||||
- quux2
|
||||
|
||||
* overwrite:
|
||||
* ``overwrite``:
|
||||
|
||||
Will use the behaviour of the 2014.1 branch and earlier.
|
||||
|
||||
|
@ -2602,14 +2606,14 @@ strategy between different sources. It accepts 4 values:
|
|||
third_key: blah
|
||||
fourth_key: blah
|
||||
|
||||
* smart (default):
|
||||
* ``smart`` (default):
|
||||
|
||||
Guesses the best strategy based on the "renderer" setting.
|
||||
|
||||
.. conf_master:: pillar_merge_lists
|
||||
|
||||
``pillar_merge_lists``
|
||||
----------------------
|
||||
**********************
|
||||
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
|
@ -2621,6 +2625,83 @@ Recursively merge lists by aggregating them instead of replacing them.
|
|||
|
||||
pillar_merge_lists: False
|
||||
|
||||
.. _pillar-cache-opts:
|
||||
|
||||
Pillar Cache Options
|
||||
--------------------
|
||||
|
||||
.. conf_master:: pillar_cache
|
||||
|
||||
``pillar_cache``
|
||||
****************
|
||||
|
||||
.. versionadded:: 2015.8.8
|
||||
|
||||
Default: ``False``
|
||||
|
||||
A master can cache pillars locally to bypass the expense of having to render them
|
||||
for each minion on every request. This feature should only be enabled in cases
|
||||
where pillar rendering time is known to be unsatisfactory and any attendant security
|
||||
concerns about storing pillars in a master cache have been addressed.
|
||||
|
||||
When enabling this feature, be certain to read through the additional ``pillar_cache_*``
|
||||
configuration options to fully understand the tunable parameters and their implications.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillar_cache: False
|
||||
|
||||
.. note::
|
||||
|
||||
Setting ``pillar_cache: True`` has no effect on
|
||||
:ref:`targeting minions with pillar <targeting-pillar>`.
|
||||
|
||||
.. conf_master:: pillar_cache_ttl
|
||||
|
||||
``pillar_cache_ttl``
|
||||
********************
|
||||
|
||||
.. versionadded:: 2015.8.8
|
||||
|
||||
Default: ``3600``
|
||||
|
||||
If and only if a master has set ``pillar_cache: True``, the cache TTL controls the amount
|
||||
of time, in seconds, before the cache is considered invalid by a master and a fresh
|
||||
pillar is recompiled and stored.
|
||||
|
||||
.. conf_master:: pillar_cache_backend
|
||||
|
||||
``pillar_cache_backend``
|
||||
************************
|
||||
|
||||
.. versionadded:: 2015.8.8
|
||||
|
||||
Default: ``disk``
|
||||
|
||||
If an only if a master has set ``pillar_cache: True``, one of several storage providers
|
||||
can be utilized:
|
||||
|
||||
* ``disk`` (default):
|
||||
|
||||
The default storage backend. This caches rendered pillars to the master cache.
|
||||
Rendered pillars are serialized and deserialized as ``msgpack`` structures for speed.
|
||||
Note that pillars are stored UNENCRYPTED. Ensure that the master cache has permissions
|
||||
set appropriately (sane defaults are provided).
|
||||
|
||||
* ``memory`` [EXPERIMENTAL]:
|
||||
|
||||
An optional backend for pillar caches which uses a pure-Python
|
||||
in-memory data structure for maximal performance. There are several caveats,
|
||||
however. First, because each master worker contains its own in-memory cache,
|
||||
there is no guarantee of cache consistency between minion requests. This
|
||||
works best in situations where the pillar rarely if ever changes. Secondly,
|
||||
and perhaps more importantly, this means that unencrypted pillars will
|
||||
be accessible to any process which can examine the memory of the ``salt-master``!
|
||||
This may represent a substantial security risk.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
pillar_cache_backend: disk
|
||||
|
||||
Syndic Server Settings
|
||||
======================
|
||||
|
|
|
@ -16,6 +16,7 @@ Full list of builtin renderer modules
|
|||
hjson
|
||||
jinja
|
||||
json
|
||||
json5
|
||||
mako
|
||||
msgpack
|
||||
py
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
====================
|
||||
salt.renderers.json5
|
||||
====================
|
||||
|
||||
.. automodule:: salt.renderers.json5
|
||||
:members:
|
|
@ -1,5 +1,5 @@
|
|||
========================
|
||||
OS Support for Cloud VMs
|
||||
Cloud deployment scripts
|
||||
========================
|
||||
|
||||
Salt Cloud works primarily by executing a script on the virtual machines as
|
||||
|
@ -14,31 +14,39 @@ script. A stable version is included with each release tarball starting with
|
|||
|
||||
https://github.com/saltstack/salt-bootstrap
|
||||
|
||||
If you do not specify a script argument, this script will be used at the
|
||||
default.
|
||||
Note that, somewhat counter-intuitively, this script is referenced as
|
||||
``bootstrap-salt`` in the configuration.
|
||||
|
||||
If the Salt Bootstrap script does not meet your needs, you may write your own.
|
||||
The script should be written in bash and is a Jinja template. Deploy scripts
|
||||
need to execute a number of functions to do a complete salt setup. These
|
||||
functions include:
|
||||
You can specify a deploy script in the cloud configuration file
|
||||
(``/etc/salt/cloud`` by default):
|
||||
|
||||
1. Install the salt minion. If this can be done via system packages this method
|
||||
is HIGHLY preferred.
|
||||
2. Add the salt minion keys before the minion is started for the first time.
|
||||
The minion keys are available as strings that can be copied into place in
|
||||
the Jinja template under the dict named "vm".
|
||||
3. Start the salt-minion daemon and enable it at startup time.
|
||||
4. Set up the minion configuration file from the "minion" data available in
|
||||
the Jinja template.
|
||||
.. code-block:: yaml
|
||||
|
||||
A good, well commented, example of this process is the Fedora deployment
|
||||
script:
|
||||
script: bootstrap-salt
|
||||
|
||||
https://github.com/saltstack/salt-cloud/blob/master/saltcloud/deploy/Fedora.sh
|
||||
|
||||
A number of legacy deploy scripts are included with the release tarball. None
|
||||
of them are as functional or complete as Salt Bootstrap, and are still included
|
||||
for academic purposes.
|
||||
Or in a provider:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-provider:
|
||||
# snip...
|
||||
script: bootstrap-salt
|
||||
|
||||
|
||||
Or in a profile:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-profile:
|
||||
provider: my-provider
|
||||
# snip...
|
||||
script: bootstrap-salt
|
||||
|
||||
|
||||
If you do not specify a script argument in your cloud configuration file,
|
||||
provider configuration or profile configuration, the "bootstrap-salt" script
|
||||
will be used by default.
|
||||
|
||||
|
||||
Other Generic Deploy Scripts
|
||||
|
@ -61,6 +69,54 @@ refit to meet your needs. One important use of them is to pass options to
|
|||
the salt-bootstrap script, such as updating to specific git tags.
|
||||
|
||||
|
||||
Custom Deploy Scripts
|
||||
=====================
|
||||
|
||||
If the Salt Bootstrap script does not meet your needs, you may write your own.
|
||||
The script should be written in shell and is a Jinja template. Deploy scripts
|
||||
need to execute a number of functions to do a complete salt setup. These
|
||||
functions include:
|
||||
|
||||
1. Install the salt minion. If this can be done via system packages this method
|
||||
is HIGHLY preferred.
|
||||
2. Add the salt minion keys before the minion is started for the first time.
|
||||
The minion keys are available as strings that can be copied into place in
|
||||
the Jinja template under the dict named "vm".
|
||||
3. Start the salt-minion daemon and enable it at startup time.
|
||||
4. Set up the minion configuration file from the "minion" data available in
|
||||
the Jinja template.
|
||||
|
||||
A good, well commented example of this process is the Fedora deployment
|
||||
script:
|
||||
|
||||
https://github.com/saltstack/salt-cloud/blob/master/saltcloud/deploy/Fedora.sh
|
||||
|
||||
A number of legacy deploy scripts are included with the release tarball. None
|
||||
of them are as functional or complete as Salt Bootstrap, and are still included
|
||||
for academic purposes.
|
||||
|
||||
Custom deploy scripts are picked up from ``/etc/salt/cloud.deploy.d`` by
|
||||
default, but you can change the location of deploy scripts with the cloud
|
||||
configuration ``deploy_scripts_search_path``. Additionally, if your deploy
|
||||
script has the extension ``.sh``, you can leave out the extension in your
|
||||
configuration.
|
||||
|
||||
For example, if your custom deploy script is located in
|
||||
``/etc/salt/cloud.deploy.d/my_deploy.sh``, you could specify it in a cloud
|
||||
profile like this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
my-profile:
|
||||
provider: my-provider
|
||||
# snip...
|
||||
script: my_deploy
|
||||
|
||||
You're also free to use the full path to the script if you like. Using full
|
||||
paths, your script doesn't have to live inside ``/etc/salt/cloud.deploy.d`` or
|
||||
whatever you've configured with ``deploy_scripts_search_path``.
|
||||
|
||||
|
||||
Post-Deploy Commands
|
||||
====================
|
||||
|
||||
|
|
|
@ -24,6 +24,18 @@ This package can be installed using `pip` or `easy_install`:
|
|||
pip install pyvmomi
|
||||
easy_install pyvmomi
|
||||
|
||||
.. note::
|
||||
|
||||
Version 6.0 of pyVmomi has some problems with SSL error handling on certain
|
||||
versions of Python. If using version 6.0 of pyVmomi, the machine that you
|
||||
are running the proxy minion process from must have either Python 2.7.9 or
|
||||
newer This is due to an upstream dependency in pyVmomi 6.0 that is not supported
|
||||
in Python version 2.6 to 2.7.8. If the version of Python running the salt-cloud
|
||||
command is not in the supported range, you will need to install an earlier version
|
||||
of pyVmomi. See `Issue #29537`_ for more information.
|
||||
|
||||
.. _Issue #29537: https://github.com/saltstack/salt/issues/29537
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
|
|
@ -274,9 +274,9 @@ with labels.
|
|||
``Awesome``
|
||||
The pull request implements an especially well crafted solution, or a very difficult but necessary change.
|
||||
|
||||
``Low Hanging Fruit``
|
||||
The issue is trivial or almost trivial to implement or fix. Issues having this label should be a good starting
|
||||
place for new contributors to Salt.
|
||||
``Help Wanted``
|
||||
The issue appears to have a simple solution. Issues having this label
|
||||
should be a good starting place for new contributors to Salt.
|
||||
|
||||
``Needs Testcase``
|
||||
The issue or pull request relates to a feature that needs test coverage. The pull request containing the tests
|
||||
|
|
|
@ -60,7 +60,7 @@ The access controls are manifested using matchers in these configurations:
|
|||
|
||||
In the above example, fred is able to send commands only to minions which match
|
||||
the specified glob target. This can be expanded to include other functions for
|
||||
other minions based on standard targets.
|
||||
other minions based on standard targets (all matchers are supported except the compound one).
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@ -84,4 +84,3 @@ unrestricted access to salt commands.
|
|||
|
||||
.. note::
|
||||
Functions are matched using regular expressions.
|
||||
|
||||
|
|
|
@ -45,10 +45,10 @@ passed, an empty list must be added:
|
|||
Mine Functions Aliases
|
||||
----------------------
|
||||
|
||||
Function aliases can be used to provide friendly names, usage intentions or to allow
|
||||
multiple calls of the same function with different arguments. There is a different
|
||||
syntax for passing positional and key-value arguments. Mixing positional and
|
||||
key-value arguments is not supported.
|
||||
Function aliases can be used to provide friendly names, usage intentions or to
|
||||
allow multiple calls of the same function with different arguments. There is a
|
||||
different syntax for passing positional and key-value arguments. Mixing
|
||||
positional and key-value arguments is not supported.
|
||||
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
|
@ -115,6 +115,20 @@ stored in a different location. Here is an example of a flat roster containing
|
|||
of the Minion in question. This results in a non-trivial delay in
|
||||
retrieving the requested data.
|
||||
|
||||
Minions Targeting with Mine
|
||||
===========================
|
||||
|
||||
The ``mine.get`` function supports various methods of :ref:`Minions targeting
|
||||
<targeting>` to fetch Mine data from particular hosts, such as glob or regular
|
||||
expression matching on Minion id (name), grains, pillars and :ref:`compound
|
||||
matches <targeting-compound>`. See the :py:mod:`salt.modules.mine` module
|
||||
documentation for the reference.
|
||||
|
||||
.. note::
|
||||
|
||||
Pillar data needs to be cached on Master for pillar targeting to work with
|
||||
Mine. Read the note in :ref:`relevant section <targeting-pillar>`.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
|
@ -160,7 +174,7 @@ to add them to the pool of load balanced servers.
|
|||
|
||||
<...file contents snipped...>
|
||||
|
||||
{% for server, addrs in salt['mine.get']('roles:web', 'network.ip_addrs', expr_form='grain').items() %}
|
||||
{% for server, addrs in salt['mine.get']('roles:web', 'network.ip_addrs', expr_form='grain') | dictsort() %}
|
||||
server {{ server }} {{ addrs[0] }}:80 check
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
@ -90,6 +90,22 @@ To execute a function, use :mod:`salt.function <salt.states.saltmod.function>`:
|
|||
|
||||
salt-run state.orchestrate orch.cleanfoo
|
||||
|
||||
If you omit the "name" argument, the ID of the state will be the default name,
|
||||
or in the case of ``salt.function``, the execution module function to run. You
|
||||
can specify the "name" argument to avoid conflicting IDs:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
copy_some_file:
|
||||
salt.function:
|
||||
- name: file.copy
|
||||
- tgt: '*'
|
||||
- arg:
|
||||
- /path/to/file
|
||||
- /tmp/copy_of_file
|
||||
- kwarg:
|
||||
- remove_existing: true
|
||||
|
||||
State
|
||||
^^^^^
|
||||
|
||||
|
@ -138,8 +154,9 @@ unless prevented from doing so by any :doc:`requisites
|
|||
|
||||
.. code-block:: yaml
|
||||
|
||||
cmd.run:
|
||||
bootstrap_servers:
|
||||
salt.function:
|
||||
- name: cmd.run
|
||||
- tgt: 10.0.0.0/24
|
||||
- tgt_type: ipcidr
|
||||
- arg:
|
||||
|
|
|
@ -12,10 +12,24 @@ features </topics/releases/2016.3.0>`! Be sure to report any bugs you find on `G
|
|||
Installing Using Packages
|
||||
=========================
|
||||
|
||||
Builds for a few platforms are available as part of the RC here:
|
||||
https://repo.saltstack.com/salt_rc/. For RHEL and Ubuntu, Follow the
|
||||
instructions on https://repo.saltstack.com/, but prepend the URL paths with
|
||||
``salt_rc/``.
|
||||
Builds for a few platforms are available as part of the RC at:
|
||||
|
||||
- https://repo.saltstack.com/salt_rc/
|
||||
- https://repo.saltstack.com/freebsd/salt_rc/
|
||||
|
||||
.. note::
|
||||
|
||||
For RHEL and Ubuntu, Follow the instructions on
|
||||
https://repo.saltstack.com/, but insert ``salt_rc/`` into the URL between
|
||||
the hostname and the remainder of the path. For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
baseurl=https://repo.saltstack.com/salt_rc/yum/redhat/$releasever/$basearch/
|
||||
|
||||
.. code-block::
|
||||
|
||||
deb http://repo.saltstack.com/salt_rc/apt/ubuntu/14.04/amd64 jessie main
|
||||
|
||||
Available builds:
|
||||
|
||||
|
@ -24,6 +38,7 @@ Available builds:
|
|||
- RHEL 6
|
||||
- RHEL 7
|
||||
- Ubuntu 14.04
|
||||
- FreeBSD
|
||||
|
||||
Installing Using Bootstrap
|
||||
==========================
|
||||
|
|
|
@ -7,6 +7,18 @@ Targeting using Pillar
|
|||
Pillar data can be used when targeting minions. This allows for ultimate
|
||||
control and flexibility when targeting minions.
|
||||
|
||||
.. note::
|
||||
|
||||
To start using Pillar targeting it is required to make a Pillar
|
||||
data cache on Salt Master for each Minion via following commands:
|
||||
``salt '*' saltutil.refresh_pillar`` or ``salt '*' saltutil.sync_all``.
|
||||
Also Pillar data cache will be populated during the
|
||||
:ref:`highstate <running-highstate>` run. Once Pillar data changes, you
|
||||
must refresh the cache by running above commands for this targeting
|
||||
method to work correctly.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt -I 'somekey:specialvalue' test.ping
|
||||
|
|
|
@ -63,26 +63,38 @@ be used to install it:
|
|||
|
||||
If pygit2_ is not packaged for the platform on which the Master is running, the
|
||||
pygit2_ website has installation instructions here__. Keep in mind however that
|
||||
following these instructions will install libgit2 and pygit2_ without system
|
||||
following these instructions will install libgit2_ and pygit2_ without system
|
||||
packages. Additionally, keep in mind that :ref:`SSH authentication in pygit2
|
||||
<pygit2-authentication-ssh>` requires libssh2_ (*not* libssh) development
|
||||
libraries to be present before libgit2 is built. On some distros (debian based)
|
||||
``pkg-config`` is also required to link libgit2 with libssh2.
|
||||
libraries to be present before libgit2_ is built. On some Debian-based distros
|
||||
``pkg-config`` is also required to link libgit2_ with libssh2.
|
||||
|
||||
Additionally, version 0.21.0 of pygit2 introduced a dependency on python-cffi_,
|
||||
which in turn depends on newer releases of libffi_. Upgrading libffi_ is not
|
||||
advisable as several other applications depend on it, so on older LTS linux
|
||||
releases pygit2_ 0.20.3 and libgit2_ 0.20.0 is the recommended combination.
|
||||
While these are not packaged in the official repositories for Debian and
|
||||
Ubuntu, SaltStack is actively working on adding packages for these to our
|
||||
repositories_. The progress of this effort can be tracked here__.
|
||||
|
||||
.. warning::
|
||||
pygit2_ is actively developed and :ref:`frequently makes
|
||||
non-backwards-compatible API changes <pygit2-version-policy>`, even in
|
||||
minor releases. It is not uncommon for pygit2_ upgrades to result in errors
|
||||
in Salt. Please take care when upgrading pygit2_, and pay close attention
|
||||
to the :ref:`changelog <pygit2-changelog>`, keeping an eye out for API
|
||||
changes. Errors can be reported on the :ref:`SaltStack issue tracker
|
||||
<saltstack-issue-tracker>`.
|
||||
to the changelog_, keeping an eye out for API changes. Errors can be
|
||||
reported on the :ref:`SaltStack issue tracker <saltstack-issue-tracker>`.
|
||||
|
||||
.. _pygit2-version-policy: http://www.pygit2.org/install.html#version-numbers
|
||||
.. _pygit2-changelog: https://github.com/libgit2/pygit2#changelog
|
||||
.. _changelog: https://github.com/libgit2/pygit2#changelog
|
||||
.. _saltstack-issue-tracker: https://github.com/saltstack/salt/issues
|
||||
.. __: http://www.pygit2.org/install.html
|
||||
.. _libgit2: https://libgit2.github.com/
|
||||
.. _libssh2: http://www.libssh2.org/
|
||||
.. _python-cffi: https://pypi.python.org/pypi/cffi
|
||||
.. _libffi: http://sourceware.org/libffi/
|
||||
.. _repositories: https://repo.saltstack.com
|
||||
.. __: https://github.com/saltstack/salt-pack/issues/70
|
||||
|
||||
GitPython
|
||||
---------
|
||||
|
@ -313,7 +325,12 @@ tremendous amount of customization. Here's some example usage:
|
|||
- https://foo.com/foo.git
|
||||
- https://foo.com/bar.git:
|
||||
- root: salt
|
||||
- mountpoint: salt://foo/bar/baz
|
||||
- mountpoint: salt://bar
|
||||
- base: salt-base
|
||||
- https://foo.com/bar.git:
|
||||
- name: second_bar_repo
|
||||
- root: other/salt
|
||||
- mountpoint: salt://other/bar
|
||||
- base: salt-base
|
||||
- http://foo.com/baz.git:
|
||||
- root: salt/states
|
||||
|
@ -330,26 +347,32 @@ tremendous amount of customization. Here's some example usage:
|
|||
with a colon.
|
||||
|
||||
2. Per-remote configuration parameters are named like the global versions,
|
||||
with the ``gitfs_`` removed from the beginning.
|
||||
with the ``gitfs_`` removed from the beginning. The exception being the
|
||||
``name`` parameter which is only available to per-remote configurations.
|
||||
|
||||
In the example configuration above, the following is true:
|
||||
|
||||
1. The first and third gitfs remotes will use the ``develop`` branch/tag as the
|
||||
``base`` environment, while the second one will use the ``salt-base``
|
||||
1. The first and fourth gitfs remotes will use the ``develop`` branch/tag as the
|
||||
``base`` environment, while the second and third will use the ``salt-base``
|
||||
branch/tag as the ``base`` environment.
|
||||
|
||||
2. The first remote will serve all files in the repository. The second
|
||||
remote will only serve files from the ``salt`` directory (and its
|
||||
subdirectories), while the third remote will only serve files from the
|
||||
``salt/states`` directory (and its subdirectories).
|
||||
subdirectories). The third remote will only server files from the
|
||||
``other/salt`` directory (and its subdirectorys), while the fourth remote
|
||||
will only serve files from the ``salt/states`` directory (and its
|
||||
subdirectories).
|
||||
|
||||
3. The files from the second remote will be located under
|
||||
``salt://foo/bar/baz``, while the files from the first and third remotes
|
||||
will be located under the root of the Salt fileserver namespace
|
||||
(``salt://``).
|
||||
3. The first and fourth remotes will have files located under the root of the
|
||||
Salt fileserver namespace (``salt://``). The files from the second remote
|
||||
will be located under ``salt://bar``, while the files from the third remote
|
||||
will be located under ``salt://other/bar``.
|
||||
|
||||
4. The third remote overrides the default behavior of :ref:`not authenticating to
|
||||
insecure (non-HTTPS) remotes <gitfs-insecure-auth>`.
|
||||
4. The second and third remotes reference the same repository and unique names
|
||||
need to be declared for duplicate gitfs remotes.
|
||||
|
||||
5. The fourth remote overrides the default behavior of :ref:`not authenticating
|
||||
to insecure (non-HTTPS) remotes <gitfs-insecure-auth>`.
|
||||
|
||||
Serving from a Subdirectory
|
||||
===========================
|
||||
|
|
|
@ -110,7 +110,6 @@ the sample configuration file (default values)
|
|||
recon_max: 5000
|
||||
recon_randomize: True
|
||||
|
||||
|
||||
- recon_default: the default value the socket should use, i.e. 1000. This value is in
|
||||
milliseconds. (1000ms = 1 second)
|
||||
- recon_max: the max value that the socket should use as a delay before trying to reconnect
|
||||
|
|
|
@ -29,8 +29,8 @@ for any OS with a Bourne shell:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -L https://bootstrap.saltstack.com -o install_salt.sh
|
||||
sudo sh install_salt.sh
|
||||
curl -L https://bootstrap.saltstack.com -o bootstrap_salt.sh
|
||||
sudo sh bootstrap_salt.sh
|
||||
|
||||
|
||||
See the `salt-bootstrap`_ documentation for other one liners. When using `Vagrant`_
|
||||
|
|
|
@ -533,8 +533,8 @@ This example clearly illustrates that; one, using the YAML renderer by default
|
|||
is a wise decision and two, unbridled power can be obtained where needed by
|
||||
using a pure Python SLS.
|
||||
|
||||
Running and debugging salt states.
|
||||
----------------------------------
|
||||
Running and Debugging Salt States
|
||||
---------------------------------
|
||||
|
||||
Once the rules in an SLS are ready, they should be tested to ensure they
|
||||
work properly. To invoke these rules, simply execute
|
||||
|
|
|
@ -129,12 +129,24 @@ modules.
|
|||
The Salt module functions are also made available in the template context as
|
||||
``salt:``
|
||||
|
||||
The following example illustrates calling the ``group_to_gid`` function in the
|
||||
``file`` execution module with a single positional argument called
|
||||
``some_group_that_exists``.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
moe:
|
||||
user.present:
|
||||
- gid: {{ salt['file.group_to_gid']('some_group_that_exists') }}
|
||||
|
||||
One way to think about this might be that the ``gid`` key is being assigned
|
||||
a value equivelent to the following python pseudo-code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import salt.modules.file
|
||||
file.group_to_gid('some_group_that_exists')
|
||||
|
||||
Note that for the above example to work, ``some_group_that_exists`` must exist
|
||||
before the state file is processed by the templating engine.
|
||||
|
||||
|
@ -145,6 +157,9 @@ MAC address for eth0:
|
|||
|
||||
salt['network.hw_addr']('eth0')
|
||||
|
||||
To examine the possible arguments to each execution module function,
|
||||
one can examine the `module reference documentation </ref/modules/all>`:
|
||||
|
||||
Advanced SLS module syntax
|
||||
==========================
|
||||
|
||||
|
|
|
@ -101,9 +101,9 @@ cp $PKGRESOURCES/scripts/com.saltstack.salt.syndic.plist $PKGDIR/Library/LaunchD
|
|||
cp $PKGRESOURCES/scripts/com.saltstack.salt.api.plist $PKGDIR/Library/LaunchDaemons
|
||||
|
||||
############################################################################
|
||||
# Remove pkg-config files from the distro
|
||||
# Remove unnecessary files from the package
|
||||
############################################################################
|
||||
echo -n -e "\033]0;Build_Pkg: Remove pkg-config files\007"
|
||||
echo -n -e "\033]0;Build_Pkg: Trim unneeded files\007"
|
||||
|
||||
sudo rm -rdf $PKGDIR/opt/salt/bin/pkg-config
|
||||
sudo rm -rdf $PKGDIR/opt/salt/lib/pkgconfig
|
||||
|
@ -111,6 +111,10 @@ sudo rm -rdf $PKGDIR/opt/salt/lib/engines
|
|||
sudo rm -rdf $PKGDIR/opt/salt/share/aclocal
|
||||
sudo rm -rdf $PKGDIR/opt/salt/share/doc
|
||||
sudo rm -rdf $PKGDIR/opt/salt/share/man/man1/pkg-config.1
|
||||
sudo rm -rdf $PKGDIR/opt/salt/lib/python2.7/test
|
||||
|
||||
echo -n -e "\033]0;Build_Pkg: Remove compiled python files\007"
|
||||
sudo find $PKGDIR/opt/salt -name '*.pyc' -type f -delete
|
||||
|
||||
############################################################################
|
||||
# Copy Additional Resources from Salt Repo to the Package Directory
|
||||
|
|
|
@ -22,5 +22,12 @@
|
|||
<key>NumberOfFiles</key>
|
||||
<integer>100000</integer>
|
||||
</dict>
|
||||
<!-- uncomment the lines below to debug launchd issues -->
|
||||
<!--
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/salt-minion.out</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/salt-minion.err</string>
|
||||
-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -22,5 +22,12 @@
|
|||
<key>NumberOfFiles</key>
|
||||
<integer>100000</integer>
|
||||
</dict>
|
||||
<!-- uncomment the lines below to debug launchd issues -->
|
||||
<!--
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/salt-minion.out</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/salt-minion.err</string>
|
||||
-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -22,5 +22,12 @@
|
|||
<key>NumberOfFiles</key>
|
||||
<integer>100000</integer>
|
||||
</dict>
|
||||
<!-- uncomment the lines below to debug launchd issues -->
|
||||
<!--
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/salt-minion.out</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/salt-minion.err</string>
|
||||
-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -22,5 +22,12 @@
|
|||
<key>NumberOfFiles</key>
|
||||
<integer>100000</integer>
|
||||
</dict>
|
||||
<!-- uncomment the lines below to debug launchd issues -->
|
||||
<!--
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/salt-minion.out</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/salt-minion.err</string>
|
||||
-->
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
### Packaged Plugin Licenses
|
||||
The following dependencies are packaged with
|
||||
salt for Windows with their corresponding licenses:
|
||||
|
||||
| Module | License |
|
||||
|-----------|---------|
|
||||
| backports-abc | --- |
|
||||
| backports.ssl-match-hostname | PSF |
|
||||
| certifi | ISC |
|
||||
| cffi | MIT |
|
||||
| CherryPy | BSD |
|
||||
| enum34 | BSD |
|
||||
| futures | BSD |
|
||||
| gitdb | BSD |
|
||||
| GitPython | BSD |
|
||||
| idna | BSD-like |
|
||||
| ioflo | Apache 2.0 |
|
||||
| ioloop | MIT |
|
||||
| ipaddress | PSF |
|
||||
| Jinja2 | BSD |
|
||||
| libnacl | --- |
|
||||
| Mako | MIT |
|
||||
| MarkupSafe | BSD |
|
||||
| msgpack-python | --- |
|
||||
| pip | MIT |
|
||||
| psutil | BSD |
|
||||
| pyasn1 | BSD |
|
||||
| pycparser | BSD |
|
||||
| pycurl | LGPL + MIT |
|
||||
| PyMySQL | MIT |
|
||||
| pypiwin32 | PSF |
|
||||
| python-dateutil | Simplified BSD |
|
||||
| python-gnupg | BSD |
|
||||
| PyYAML | MIT |
|
||||
| pyzmq | LGPL + BSD |
|
||||
| requests | Apache 2.0 |
|
||||
| setuptools | MIT |
|
||||
| singledispatch | MIT |
|
||||
| six | MIT |
|
||||
| smmap | BSD |
|
||||
| timelib | ZLIB/PHP |
|
||||
| tornado | Apache 2.0 |
|
||||
| wheel | MIT |
|
||||
| WMI | MIT |
|
|
@ -3,19 +3,23 @@ backports.ssl-match-hostname==3.5.0.1
|
|||
certifi
|
||||
cffi==1.5.0
|
||||
CherryPy==5.0.1
|
||||
enum34==1.1.3
|
||||
futures==3.0.4
|
||||
gitdb==0.6.4
|
||||
GitPython==1.0.1
|
||||
idna==2.0
|
||||
ioflo==1.5.0
|
||||
ioloop==0.1a0
|
||||
ipaddress==1.0.16
|
||||
Jinja2==2.8
|
||||
libnacl==1.4.4
|
||||
Mako==1.0.3
|
||||
MarkupSafe==0.23
|
||||
msgpack-python==0.4.7
|
||||
psutil==3.4.2
|
||||
pyasn1==0.1.9
|
||||
pycparser==2.14
|
||||
pycurl==7.43.0
|
||||
PyMySQL==0.7.1
|
||||
pypiwin32==219
|
||||
python-dateutil==2.4.2
|
||||
|
@ -26,7 +30,7 @@ requests==2.9.1
|
|||
singledispatch==3.4.0.3
|
||||
six==1.10.0
|
||||
smmap==0.9.0
|
||||
timelib==0.2.4
|
||||
timelib==0.2.5
|
||||
tornado==4.3
|
||||
wheel==0.26.0
|
||||
WMI==1.4.9
|
||||
|
|
|
@ -49,7 +49,7 @@ try:
|
|||
except ImportError as exc:
|
||||
if exc.args[0] != 'No module named _msgpack':
|
||||
raise
|
||||
from salt.exceptions import SaltSystemExit
|
||||
from salt.exceptions import SaltSystemExit, SaltClientError, get_error_message
|
||||
|
||||
|
||||
# Let's instantiate log using salt.log.setup.logging.getLogger() so pylint
|
||||
|
@ -105,7 +105,7 @@ class DaemonsMixin(object): # pylint: disable=no-init
|
|||
:return:
|
||||
'''
|
||||
log.exception('Failed to create environment for {d_name}: {reason}'.format(
|
||||
d_name=self.__class__.__name__, reason=error.message))
|
||||
d_name=self.__class__.__name__, reason=get_error_message(error)))
|
||||
self.shutdown(error)
|
||||
|
||||
|
||||
|
@ -354,6 +354,8 @@ class Minion(parsers.MinionOptionParser, DaemonsMixin): # pylint: disable=no-in
|
|||
self.action_log_info('Starting up')
|
||||
self.verify_hash_type()
|
||||
self.minion.tune_in()
|
||||
if self.minion.restart:
|
||||
raise SaltClientError('Minion could not connect to Master')
|
||||
except (KeyboardInterrupt, SaltSystemExit) as error:
|
||||
self.action_log_info('Stopping')
|
||||
if isinstance(error, KeyboardInterrupt):
|
||||
|
|
|
@ -417,7 +417,7 @@ class LocalClient(object):
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
>>> returns = local.cmd_batch('*', 'state.highstate', bat='10%')
|
||||
>>> returns = local.cmd_batch('*', 'state.highstate', batch='10%')
|
||||
>>> for ret in returns:
|
||||
... print(ret)
|
||||
{'jerry': {...}}
|
||||
|
|
|
@ -103,8 +103,8 @@ class FunctionWrapper(object):
|
|||
The remote execution function
|
||||
'''
|
||||
argv = [cmd]
|
||||
argv.extend([str(arg) for arg in args])
|
||||
argv.extend(['{0}={1}'.format(key, val) for key, val in six.iteritems(kwargs)])
|
||||
argv.extend([json.dumps(arg) for arg in args])
|
||||
argv.extend(['{0}={1}'.format(key, json.dumps(val)) for key, val in six.iteritems(kwargs)])
|
||||
single = salt.client.ssh.Single(
|
||||
self.opts,
|
||||
argv,
|
||||
|
|
|
@ -50,6 +50,7 @@ import salt.utils
|
|||
|
||||
# Import salt.cloud libs
|
||||
from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import
|
||||
from salt.utils import namespaced_function
|
||||
import salt.utils.cloud
|
||||
import salt.config as config
|
||||
from salt.exceptions import (
|
||||
|
@ -64,6 +65,24 @@ try:
|
|||
except ImportError:
|
||||
HAS_NETADDR = False
|
||||
|
||||
|
||||
# Some of the libcloud functions need to be in the same namespace as the
|
||||
# functions defined in the module, so we create new function objects inside
|
||||
# this module namespace
|
||||
get_size = namespaced_function(get_size, globals())
|
||||
get_image = namespaced_function(get_image, globals())
|
||||
avail_locations = namespaced_function(avail_locations, globals())
|
||||
avail_images = namespaced_function(avail_images, globals())
|
||||
avail_sizes = namespaced_function(avail_sizes, globals())
|
||||
script = namespaced_function(script, globals())
|
||||
destroy = namespaced_function(destroy, globals())
|
||||
reboot = namespaced_function(reboot, globals())
|
||||
list_nodes = namespaced_function(list_nodes, globals())
|
||||
list_nodes_full = namespaced_function(list_nodes_full, globals())
|
||||
list_nodes_select = namespaced_function(list_nodes_select, globals())
|
||||
show_instance = namespaced_function(show_instance, globals())
|
||||
get_node = namespaced_function(get_node, globals())
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -72,7 +91,7 @@ __virtualname__ = 'dimensiondata'
|
|||
|
||||
def __virtual__():
|
||||
'''
|
||||
Set up the libcloud functions and check for GCE configurations.
|
||||
Set up the libcloud functions and check for dimensiondata configurations.
|
||||
'''
|
||||
if get_configured_provider() is False:
|
||||
return False
|
||||
|
|
|
@ -221,9 +221,9 @@ def list_nodes(conn=None, call=None):
|
|||
ret = {}
|
||||
nodes = list_nodes_full(conn, call)
|
||||
for node in nodes:
|
||||
ret[node] = {}
|
||||
for prop in ('id', 'image', 'name', 'size', 'state', 'private_ips', 'public_ips'):
|
||||
ret[node][prop] = nodes[node][prop]
|
||||
ret[node] = {'name': node}
|
||||
for prop in ('id', 'image', 'size', 'state', 'private_ips', 'public_ips'):
|
||||
ret[node][prop] = nodes[node].get(prop)
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -585,6 +585,7 @@ def create(vm_):
|
|||
# Deleting two useless keywords
|
||||
del vm_kwargs['deployment_slot']
|
||||
del vm_kwargs['label']
|
||||
del vm_kwargs['virtual_network_name']
|
||||
result = conn.add_role(**vm_kwargs)
|
||||
_wait_for_async(conn, result.request_id)
|
||||
except Exception as exc:
|
||||
|
|
|
@ -865,7 +865,7 @@ DEFAULT_MINION_OPTS = {
|
|||
'environment': None,
|
||||
'pillarenv': None,
|
||||
'pillar_opts': False,
|
||||
# `pillar_cache` and `pillar_ttl`
|
||||
# ``pillar_cache``, ``pillar_cache_ttl`` and ``pillar_cache_backend``
|
||||
# are not used on the minion but are unavoidably in the code path
|
||||
'pillar_cache': False,
|
||||
'pillar_cache_ttl': 3600,
|
||||
|
@ -925,8 +925,8 @@ DEFAULT_MINION_OPTS = {
|
|||
'gitfs_passphrase': '',
|
||||
'gitfs_env_whitelist': [],
|
||||
'gitfs_env_blacklist': [],
|
||||
'gitfs_ssl_verify': True,
|
||||
'gitfs_global_lock': True,
|
||||
'gitfs_ssl_verify': True,
|
||||
'hash_type': 'md5',
|
||||
'disable_modules': [],
|
||||
'disable_returners': [],
|
||||
|
@ -1120,8 +1120,8 @@ DEFAULT_MASTER_OPTS = {
|
|||
'gitfs_passphrase': '',
|
||||
'gitfs_env_whitelist': [],
|
||||
'gitfs_env_blacklist': [],
|
||||
'gitfs_ssl_verify': True,
|
||||
'gitfs_global_lock': True,
|
||||
'gitfs_ssl_verify': True,
|
||||
'hgfs_remotes': [],
|
||||
'hgfs_mountpoint': '',
|
||||
'hgfs_root': '',
|
||||
|
|
|
@ -26,6 +26,13 @@ def _nested_output(obj):
|
|||
return ret
|
||||
|
||||
|
||||
def get_error_message(error):
|
||||
'''
|
||||
Get human readable message from Python Exception
|
||||
'''
|
||||
return error.args[0] if error.args else ''
|
||||
|
||||
|
||||
class SaltException(Exception):
|
||||
'''
|
||||
Base exception class; all Salt-specific exceptions should subclass this
|
||||
|
|
|
@ -1273,14 +1273,19 @@ def os_data():
|
|||
for line in fhr:
|
||||
if 'enterprise' in line.lower():
|
||||
grains['lsb_distrib_id'] = 'SLES'
|
||||
grains['lsb_distrib_codename'] = re.sub(r'\(.+\)', '', line).strip()
|
||||
elif 'version' in line.lower():
|
||||
version = re.sub(r'[^0-9]', '', line)
|
||||
elif 'patchlevel' in line.lower():
|
||||
patch = re.sub(r'[^0-9]', '', line)
|
||||
grains['lsb_distrib_release'] = version
|
||||
if patch:
|
||||
grains['lsb_distrib_release'] += ' SP' + patch
|
||||
grains['lsb_distrib_codename'] = 'n.a'
|
||||
grains['lsb_distrib_release'] += '.' + patch
|
||||
patchstr = 'SP' + patch
|
||||
if grains['lsb_distrib_codename'] and patchstr not in grains['lsb_distrib_codename']:
|
||||
grains['lsb_distrib_codename'] += ' ' + patchstr
|
||||
if not grains['lsb_distrib_codename']:
|
||||
grains['lsb_distrib_codename'] = 'n.a'
|
||||
elif os.path.isfile('/etc/altlinux-release'):
|
||||
# ALT Linux
|
||||
grains['lsb_distrib_id'] = 'altlinux'
|
||||
|
|
|
@ -634,12 +634,12 @@ def grains(opts, force_refresh=False, proxy=None):
|
|||
print __grains__['id']
|
||||
'''
|
||||
# if we hae no grains, lets try loading from disk (TODO: move to decorator?)
|
||||
cfn = os.path.join(
|
||||
opts['cachedir'],
|
||||
'grains.cache.p'
|
||||
)
|
||||
if not force_refresh:
|
||||
if opts.get('grains_cache', False):
|
||||
cfn = os.path.join(
|
||||
opts['cachedir'],
|
||||
'grains.cache.p'
|
||||
)
|
||||
if os.path.isfile(cfn):
|
||||
grains_cache_age = int(time.time() - os.path.getmtime(cfn))
|
||||
if opts.get('grains_cache_expiration', 300) >= grains_cache_age and not \
|
||||
|
|
|
@ -820,6 +820,7 @@ class Minion(MinionBase):
|
|||
self.win_proc = []
|
||||
self.loaded_base_name = loaded_base_name
|
||||
self.connected = False
|
||||
self.restart = False
|
||||
|
||||
if io_loop is None:
|
||||
if HAS_ZMQ:
|
||||
|
@ -1858,9 +1859,13 @@ class Minion(MinionBase):
|
|||
|
||||
# if eval_master finds a new master for us, self.connected
|
||||
# will be True again on successful master authentication
|
||||
master, self.pub_channel = yield self.eval_master(
|
||||
opts=self.opts,
|
||||
failed=True)
|
||||
try:
|
||||
master, self.pub_channel = yield self.eval_master(
|
||||
opts=self.opts,
|
||||
failed=True)
|
||||
except SaltClientError:
|
||||
pass
|
||||
|
||||
if self.connected:
|
||||
self.opts['master'] = master
|
||||
|
||||
|
@ -1898,6 +1903,9 @@ class Minion(MinionBase):
|
|||
schedule=schedule)
|
||||
else:
|
||||
self.schedule.delete_job(name='__master_failback', persist=True)
|
||||
else:
|
||||
self.restart = True
|
||||
self.io_loop.stop()
|
||||
|
||||
elif tag.startswith('__master_connected'):
|
||||
# handle this event only once. otherwise it will pollute the log
|
||||
|
@ -2049,6 +2057,8 @@ class Minion(MinionBase):
|
|||
if start:
|
||||
try:
|
||||
self.io_loop.start()
|
||||
if self.restart:
|
||||
self.destroy()
|
||||
except (KeyboardInterrupt, RuntimeError): # A RuntimeError can be re-raised by Tornado on shutdown
|
||||
self.destroy()
|
||||
|
||||
|
|
|
@ -1702,7 +1702,7 @@ def mod_repo(repo, saltenv='base', **kwargs):
|
|||
enabled configuration. Anything supplied for this list will be saved
|
||||
in the repo configuration with a comment marker (#) in front.
|
||||
|
||||
.. versionadded:: 2016.3.1
|
||||
.. versionadded:: 2015.8.9
|
||||
|
||||
.. note:: Due to the way keys are stored for APT, there is a known issue
|
||||
where the key won't be updated unless another change is made
|
||||
|
|
|
@ -415,6 +415,7 @@ def enable_beacon(name, **kwargs):
|
|||
ret['comment'] = 'Beacon {0} is not currently configured.'.format(name)
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
try:
|
||||
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
||||
res = __salt__['event.fire']({'func': 'enable_beacon', 'name': name}, 'manage_beacons')
|
||||
|
@ -467,6 +468,7 @@ def disable_beacon(name, **kwargs):
|
|||
ret['comment'] = 'Beacon {0} is not currently configured.'.format(name)
|
||||
ret['result'] = False
|
||||
return ret
|
||||
|
||||
try:
|
||||
eventer = salt.utils.event.get_event('minion', opts=__opts__)
|
||||
res = __salt__['event.fire']({'func': 'disable_beacon', 'name': name}, 'manage_beacons')
|
||||
|
|
|
@ -12,6 +12,7 @@ import json
|
|||
import logging
|
||||
import salt.utils
|
||||
import salt.utils.http
|
||||
from salt.exceptions import get_error_message
|
||||
|
||||
|
||||
__proxyenabled__ = ['chronos']
|
||||
|
@ -109,10 +110,10 @@ def update_job(name, config):
|
|||
log.debug('update response: %s', response)
|
||||
return {'success': True}
|
||||
except Exception as ex:
|
||||
log.error('unable to update chronos job: %s', ex.message)
|
||||
log.error('unable to update chronos job: %s', get_error_message(ex))
|
||||
return {
|
||||
'exception': {
|
||||
'message': ex.message,
|
||||
'message': get_error_message(ex),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import salt.utils.url
|
|||
import salt.crypt
|
||||
import salt.transport
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=import-error,no-name-in-module
|
||||
|
||||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
|
@ -358,6 +359,25 @@ def cache_file(path, saltenv='base', env=None):
|
|||
# Backwards compatibility
|
||||
saltenv = env
|
||||
|
||||
contextkey = '{0}_|-{1}_|-{2}'.format('cp.cache_file', path, saltenv)
|
||||
path_is_remote = _urlparse(path).scheme in ('http', 'https', 'ftp')
|
||||
try:
|
||||
if path_is_remote and contextkey in __context__:
|
||||
# Prevent multiple caches in the same salt run. Affects remote URLs
|
||||
# since the master won't know their hash, so the fileclient
|
||||
# wouldn't be able to prevent multiple caches if we try to cache
|
||||
# the remote URL more than once.
|
||||
if os.path.isfile(__context__[contextkey]):
|
||||
return __context__[contextkey]
|
||||
else:
|
||||
# File is in __context__ but no longer exists in the minion
|
||||
# cache, get rid of the context key and re-cache below.
|
||||
# Accounts for corner case where file is removed from minion
|
||||
# cache between cp.cache_file calls in the same salt-run.
|
||||
__context__.pop(contextkey)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
_mk_client()
|
||||
|
||||
path, senv = salt.utils.url.split_env(path)
|
||||
|
@ -371,6 +391,10 @@ def cache_file(path, saltenv='base', env=None):
|
|||
path, saltenv
|
||||
)
|
||||
)
|
||||
if path_is_remote:
|
||||
# Cache was successful, store the result in __context__ to prevent
|
||||
# multiple caches (see above).
|
||||
__context__[contextkey] = result
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ import salt.utils.filebuffer
|
|||
import salt.utils.files
|
||||
import salt.utils.atomicfile
|
||||
import salt.utils.url
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError, get_error_message as _get_error_message
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -1364,7 +1364,7 @@ def _regex_to_static(src, regex):
|
|||
try:
|
||||
src = re.search(regex, src)
|
||||
except Exception as ex:
|
||||
raise CommandExecutionError("{0}: '{1}'".format(ex.message, regex))
|
||||
raise CommandExecutionError("{0}: '{1}'".format(_get_error_message(ex), regex))
|
||||
|
||||
return src and src.group() or regex
|
||||
|
||||
|
@ -3305,6 +3305,10 @@ def source_list(source, source_hash, saltenv):
|
|||
|
||||
salt '*' file.source_list salt://http/httpd.conf '{hash_type: 'md5', 'hsum': <md5sum>}' base
|
||||
'''
|
||||
contextkey = '{0}_|-{1}_|-{2}'.format(source, source_hash, saltenv)
|
||||
if contextkey in __context__:
|
||||
return __context__[contextkey]
|
||||
|
||||
# get the master file list
|
||||
if isinstance(source, list):
|
||||
mfiles = [(f, saltenv) for f in __salt__['cp.list_master'](saltenv)]
|
||||
|
@ -3338,10 +3342,7 @@ def source_list(source, source_hash, saltenv):
|
|||
ret = (single_src, single_hash)
|
||||
break
|
||||
elif proto.startswith('http') or proto == 'ftp':
|
||||
dest = salt.utils.mkstemp()
|
||||
fn_ = __salt__['cp.get_url'](single_src, dest)
|
||||
os.remove(fn_)
|
||||
if fn_:
|
||||
if __salt__['cp.cache_file'](single_src):
|
||||
ret = (single_src, single_hash)
|
||||
break
|
||||
elif proto == 'file' and os.path.exists(urlparsed_single_src.path):
|
||||
|
@ -3357,11 +3358,16 @@ def source_list(source, source_hash, saltenv):
|
|||
if (path, senv) in mfiles or (path, senv) in mdirs:
|
||||
ret = (single, source_hash)
|
||||
break
|
||||
urlparsed_source = _urlparse(single)
|
||||
if urlparsed_source.scheme == 'file' and os.path.exists(urlparsed_source.path):
|
||||
urlparsed_src = _urlparse(single)
|
||||
proto = urlparsed_src.scheme
|
||||
if proto == 'file' and os.path.exists(urlparsed_src.path):
|
||||
ret = (single, source_hash)
|
||||
break
|
||||
if single.startswith('/') and os.path.exists(single):
|
||||
elif proto.startswith('http') or proto == 'ftp':
|
||||
if __salt__['cp.cache_file'](single):
|
||||
ret = (single, source_hash)
|
||||
break
|
||||
elif single.startswith('/') and os.path.exists(single):
|
||||
ret = (single, source_hash)
|
||||
break
|
||||
if ret is None:
|
||||
|
@ -3369,10 +3375,11 @@ def source_list(source, source_hash, saltenv):
|
|||
raise CommandExecutionError(
|
||||
'none of the specified sources were found'
|
||||
)
|
||||
else:
|
||||
return ret
|
||||
else:
|
||||
return source, source_hash
|
||||
ret = (source, source_hash)
|
||||
|
||||
__context__[contextkey] = ret
|
||||
return ret
|
||||
|
||||
|
||||
def apply_template_on_contents(
|
||||
|
|
|
@ -113,7 +113,6 @@ def uninstall(cert_name,
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.install test.p12 test123
|
||||
|
||||
'''
|
||||
if keychain_password is not None:
|
||||
unlock_keychain(keychain, keychain_password)
|
||||
|
@ -135,7 +134,6 @@ def list_certs(keychain="/Library/Keychains/System.keychain"):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.list_certs
|
||||
|
||||
'''
|
||||
cmd = 'security find-certificate -a {0} | grep -o "alis".*\\" | ' \
|
||||
'grep -o \'\\"[-A-Za-z0-9.:() ]*\\"\''.format(_quote(keychain))
|
||||
|
@ -162,7 +160,6 @@ def get_friendly_name(cert, password):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.get_friendly_name /tmp/test.p12 test123
|
||||
|
||||
'''
|
||||
cmd = 'openssl pkcs12 -in {0} -passin pass:{1} -info -nodes -nokeys 2> /dev/null | ' \
|
||||
'grep friendlyName:'.format(_quote(cert), _quote(password))
|
||||
|
@ -185,7 +182,6 @@ def get_default_keychain(user=None, domain="user"):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.get_default_keychain
|
||||
|
||||
'''
|
||||
cmd = "security default-keychain -d {0}".format(domain)
|
||||
return __salt__['cmd.run'](cmd, runas=user)
|
||||
|
@ -209,7 +205,6 @@ def set_default_keychain(keychain, domain="user", user=None):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.set_keychain /Users/fred/Library/Keychains/login.keychain
|
||||
|
||||
'''
|
||||
cmd = "security default-keychain -d {0} -s {1}".format(domain, keychain)
|
||||
return __salt__['cmd.run'](cmd, runas=user)
|
||||
|
@ -233,7 +228,6 @@ def unlock_keychain(keychain, password):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.unlock_keychain /tmp/test.p12 test123
|
||||
|
||||
'''
|
||||
cmd = 'security unlock-keychain -p {0} {1}'.format(password, keychain)
|
||||
__salt__['cmd.run'](cmd)
|
||||
|
@ -250,6 +244,12 @@ def get_hash(name, password=None):
|
|||
password
|
||||
The password that is used in the certificate. Only required if your passing a p12 file.
|
||||
Note: This will be outputted to logs
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' keychain.get_hash /tmp/test.p12 test123
|
||||
'''
|
||||
|
||||
if '.p12' in name[-4:]:
|
||||
|
|
|
@ -25,7 +25,7 @@ from salt.ext.six import string_types
|
|||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.decorators as decorators
|
||||
from salt.utils.locales import sdecode
|
||||
from salt.utils.locales import sdecode as _sdecode
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -140,7 +140,7 @@ def delete(name, remove=False, force=False):
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' user.delete foo
|
||||
salt '*' user.delete name remove=True force=True
|
||||
'''
|
||||
if salt.utils.contains_whitespace(name):
|
||||
raise SaltInvocationError('Username cannot contain whitespace')
|
||||
|
@ -298,12 +298,12 @@ def chfullname(name, fullname):
|
|||
salt '*' user.chfullname foo 'Foo Bar'
|
||||
'''
|
||||
if isinstance(fullname, string_types):
|
||||
fullname = sdecode(fullname)
|
||||
fullname = _sdecode(fullname)
|
||||
pre_info = info(name)
|
||||
if not pre_info:
|
||||
raise CommandExecutionError('User \'{0}\' does not exist'.format(name))
|
||||
if isinstance(pre_info['fullname'], string_types):
|
||||
pre_info['fullname'] = sdecode(pre_info['fullname'])
|
||||
pre_info['fullname'] = _sdecode(pre_info['fullname'])
|
||||
if fullname == pre_info['fullname']:
|
||||
return True
|
||||
_dscl(
|
||||
|
@ -319,7 +319,7 @@ def chfullname(name, fullname):
|
|||
|
||||
current = info(name).get('fullname')
|
||||
if isinstance(current, string_types):
|
||||
current = sdecode(current)
|
||||
current = _sdecode(current)
|
||||
return current == fullname
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import json
|
|||
import logging
|
||||
import salt.utils
|
||||
import salt.utils.http
|
||||
from salt.exceptions import get_error_message
|
||||
|
||||
|
||||
__proxyenabled__ = ['marathon']
|
||||
|
@ -125,10 +126,10 @@ def update_app(id, config):
|
|||
log.debug('update response: %s', response)
|
||||
return response['dict']
|
||||
except Exception as ex:
|
||||
log.error('unable to update marathon app: %s', ex.message)
|
||||
log.error('unable to update marathon app: %s', get_error_message(ex))
|
||||
return {
|
||||
'exception': {
|
||||
'message': ex.message,
|
||||
'message': get_error_message(ex),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import json
|
|||
|
||||
# Import salt libs
|
||||
from salt.ext.six import string_types
|
||||
from salt.exceptions import get_error_message as _get_error_message
|
||||
|
||||
|
||||
# Import third party libs
|
||||
|
@ -428,6 +429,16 @@ def insert(objects, collection, user=None, password=None,
|
|||
|
||||
def find(collection, query=None, user=None, password=None,
|
||||
host=None, port=None, database='admin'):
|
||||
"""
|
||||
Find an object or list of objects in a collection
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' mongodb.find mycollection '[{"foo": "FOO", "bar": "BAR"}]' <user> <password> <host> <port> <database>
|
||||
|
||||
"""
|
||||
conn = _connect(user, password, host, port, database)
|
||||
if not conn:
|
||||
return 'Failed to connect to mongo database'
|
||||
|
@ -444,7 +455,7 @@ def find(collection, query=None, user=None, password=None,
|
|||
ret = col.find(query)
|
||||
return list(ret)
|
||||
except pymongo.errors.PyMongoError as err:
|
||||
log.error("Removing objects failed with error: %s", err)
|
||||
log.error("Searching objects failed with error: %s", err)
|
||||
return err
|
||||
|
||||
|
||||
|
@ -467,7 +478,7 @@ def remove(collection, query=None, user=None, password=None,
|
|||
try:
|
||||
query = _to_dict(query)
|
||||
except Exception as err:
|
||||
return err.message
|
||||
return _get_error_message(err)
|
||||
|
||||
try:
|
||||
log.info("Removing %r from %s", query, collection)
|
||||
|
@ -476,5 +487,5 @@ def remove(collection, query=None, user=None, password=None,
|
|||
ret = col.remove(query, w=w)
|
||||
return "{0} objects removed".format(ret['n'])
|
||||
except pymongo.errors.PyMongoError as err:
|
||||
log.error("Removing objects failed with error: %s", err.message)
|
||||
return err.message
|
||||
log.error("Removing objects failed with error: %s", _get_error_message(err))
|
||||
return _get_error_message(err)
|
||||
|
|
|
@ -26,6 +26,7 @@ from salt.modules.inspectlib.exceptions import (InspectorQueryException,
|
|||
import salt.utils
|
||||
import salt.utils.fsutils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.exceptions import get_error_message as _get_error_message
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -92,7 +93,7 @@ def inspect(mode='all', priority=19, **kwargs):
|
|||
except InspectorSnapshotException as ex:
|
||||
raise CommandExecutionError(ex)
|
||||
except Exception as ex:
|
||||
log.error(ex.message)
|
||||
log.error(_get_error_message(ex))
|
||||
raise Exception(ex)
|
||||
|
||||
|
||||
|
@ -157,5 +158,5 @@ def query(scope, **kwargs):
|
|||
except InspectorQueryException as ex:
|
||||
raise CommandExecutionError(ex)
|
||||
except Exception as ex:
|
||||
log.error(ex.message)
|
||||
log.error(_get_error_message(ex))
|
||||
raise Exception(ex)
|
||||
|
|
|
@ -599,7 +599,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
|
|||
cur_version = __salt__['pip.version'](bin_env)
|
||||
if not salt.utils.compare_versions(ver1=cur_version, oper='>=',
|
||||
ver2=min_version):
|
||||
log.error(
|
||||
logger.error(
|
||||
('The --use-wheel option is only supported in pip {0} and '
|
||||
'newer. The version of pip detected is {1}. This option '
|
||||
'will be ignored.'.format(min_version, cur_version))
|
||||
|
@ -612,7 +612,7 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
|
|||
cur_version = __salt__['pip.version'](bin_env)
|
||||
if not salt.utils.compare_versions(ver1=cur_version, oper='>=',
|
||||
ver2=min_version):
|
||||
log.error(
|
||||
logger.error(
|
||||
('The --no-use-wheel option is only supported in pip {0} and '
|
||||
'newer. The version of pip detected is {1}. This option '
|
||||
'will be ignored.'.format(min_version, cur_version))
|
||||
|
|
|
@ -129,6 +129,12 @@ def get_http_proxy(network_service="Ethernet"):
|
|||
|
||||
network_service
|
||||
The network service to apply the changes to, this only necessary on OSX
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.get_http_proxy Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _get_proxy_windows(['http'])
|
||||
|
@ -159,6 +165,12 @@ def set_http_proxy(server, port, user=None, password=None, network_service="Ethe
|
|||
bypass_hosts
|
||||
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
|
||||
set_proxy_bypass to edit the bypass hosts.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.set_http_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _set_proxy_windows(server, port, ['http'], bypass_hosts)
|
||||
|
@ -172,6 +184,12 @@ def get_https_proxy(network_service="Ethernet"):
|
|||
|
||||
network_service
|
||||
The network service to apply the changes to, this only necessary on OSX
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.get_https_proxy Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _get_proxy_windows(['https'])
|
||||
|
@ -202,6 +220,12 @@ def set_https_proxy(server, port, user=None, password=None, network_service="Eth
|
|||
bypass_hosts
|
||||
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
|
||||
set_proxy_bypass to edit the bypass hosts.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.set_https_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _set_proxy_windows(server, port, ['https'], bypass_hosts)
|
||||
|
@ -215,6 +239,12 @@ def get_ftp_proxy(network_service="Ethernet"):
|
|||
|
||||
network_service
|
||||
The network service to apply the changes to, this only necessary on OSX
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.get_ftp_proxy Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _get_proxy_windows(['ftp'])
|
||||
|
@ -244,6 +274,12 @@ def set_ftp_proxy(server, port, user=None, password=None, network_service="Ether
|
|||
bypass_hosts
|
||||
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
|
||||
set_proxy_bypass to edit the bypass hosts.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.set_ftp_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _set_proxy_windows(server, port, ['ftp'], bypass_hosts)
|
||||
|
@ -317,6 +353,12 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
|
|||
|
||||
bypass_hosts
|
||||
The hosts that are allowed to by pass the proxy.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.set_http_proxy example.com 1080 types="['http', 'https']"
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _set_proxy_windows(server, port, types, bypass_hosts)
|
||||
|
@ -325,6 +367,12 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
|
|||
def get_proxy_win():
|
||||
'''
|
||||
Gets all of the proxy settings in one call, only available on Windows
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' proxy.get_proxy_win
|
||||
'''
|
||||
if __grains__['os'] == 'Windows':
|
||||
return _get_proxy_windows()
|
||||
|
|
|
@ -49,6 +49,7 @@ import salt.ext.six as six
|
|||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.utils import locales
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -81,10 +82,10 @@ def _get_gecos(name):
|
|||
# Assign empty strings for any unspecified trailing GECOS fields
|
||||
while len(gecos_field) < 4:
|
||||
gecos_field.append('')
|
||||
return {'fullname': str(gecos_field[0]),
|
||||
'roomnumber': str(gecos_field[1]),
|
||||
'workphone': str(gecos_field[2]),
|
||||
'homephone': str(gecos_field[3])}
|
||||
return {'fullname': locales.sdecode(gecos_field[0]),
|
||||
'roomnumber': locales.sdecode(gecos_field[1]),
|
||||
'workphone': locales.sdecode(gecos_field[2]),
|
||||
'homephone': locales.sdecode(gecos_field[3])}
|
||||
|
||||
|
||||
def _build_gecos(gecos_dict):
|
||||
|
@ -92,7 +93,7 @@ def _build_gecos(gecos_dict):
|
|||
Accepts a dictionary entry containing GECOS field names and their values,
|
||||
and returns a full GECOS comment string, to be used with pw usermod.
|
||||
'''
|
||||
return '{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''),
|
||||
return u'{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''),
|
||||
gecos_dict.get('roomnumber', ''),
|
||||
gecos_dict.get('workphone', ''),
|
||||
gecos_dict.get('homephone', ''))
|
||||
|
@ -448,7 +449,7 @@ def get_loginclass(name):
|
|||
|
||||
'''
|
||||
|
||||
userinfo = __salt__['cmd.run_stdout']('pw usershow -n {0}'.format(name))
|
||||
userinfo = __salt__['cmd.run_stdout'](['pw', 'usershow', '-n', name])
|
||||
userinfo = userinfo.split(':')
|
||||
|
||||
return {'loginclass': userinfo[4] if len(userinfo) == 10 else ''}
|
||||
|
|
|
@ -23,6 +23,7 @@ import salt.utils
|
|||
import salt.utils.decorators as decorators
|
||||
from salt.ext import six
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.utils import locales
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -51,10 +52,10 @@ def _get_gecos(name):
|
|||
# Assign empty strings for any unspecified trailing GECOS fields
|
||||
while len(gecos_field) < 4:
|
||||
gecos_field.append('')
|
||||
return {'fullname': str(gecos_field[0]),
|
||||
'roomnumber': str(gecos_field[1]),
|
||||
'workphone': str(gecos_field[2]),
|
||||
'homephone': str(gecos_field[3])}
|
||||
return {'fullname': locales.sdecode(gecos_field[0]),
|
||||
'roomnumber': locales.sdecode(gecos_field[1]),
|
||||
'workphone': locales.sdecode(gecos_field[2]),
|
||||
'homephone': locales.sdecode(gecos_field[3])}
|
||||
|
||||
|
||||
def _build_gecos(gecos_dict):
|
||||
|
@ -62,7 +63,7 @@ def _build_gecos(gecos_dict):
|
|||
Accepts a dictionary entry containing GECOS field names and their values,
|
||||
and returns a full GECOS comment string, to be used with usermod.
|
||||
'''
|
||||
return '{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''),
|
||||
return u'{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''),
|
||||
gecos_dict.get('roomnumber', ''),
|
||||
gecos_dict.get('workphone', ''),
|
||||
gecos_dict.get('homephone', ''))
|
||||
|
|
|
@ -155,7 +155,7 @@ class daclConstants(object):
|
|||
'THIS FOLDER ONLY': {
|
||||
'TEXT': 'this file/folder only',
|
||||
'BITS': win32security.NO_INHERITANCE},
|
||||
'THIS FOLDER, SUBFOLDERS, and FILES': {
|
||||
'THIS FOLDER, SUBFOLDERS, AND FILES': {
|
||||
'TEXT': 'this folder, subfolders, and files',
|
||||
'BITS': win32security.CONTAINER_INHERIT_ACE |
|
||||
win32security.OBJECT_INHERIT_ACE},
|
||||
|
|
|
@ -1023,7 +1023,10 @@ def install(name=None,
|
|||
log.warning('"version" parameter will be ignored for multiple '
|
||||
'package targets')
|
||||
|
||||
old = list_pkgs()
|
||||
old = list_pkgs(versions_as_list=False)
|
||||
# Use of __context__ means no duplicate work here, just accessing
|
||||
# information already in __context__ from the previous call to list_pkgs()
|
||||
old_as_list = list_pkgs(versions_as_list=True)
|
||||
targets = []
|
||||
downgrade = []
|
||||
to_reinstall = {}
|
||||
|
@ -1095,20 +1098,54 @@ def install(name=None,
|
|||
else:
|
||||
pkgstr = pkgpath
|
||||
|
||||
cver = old.get(pkgname, '')
|
||||
if reinstall and cver \
|
||||
and salt.utils.compare_versions(ver1=version_num,
|
||||
oper='==',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
to_reinstall[pkgname] = pkgstr
|
||||
elif not cver or salt.utils.compare_versions(ver1=version_num,
|
||||
oper='>=',
|
||||
ver2=cver,
|
||||
cmp_func=version_cmp):
|
||||
targets.append(pkgstr)
|
||||
# Lambda to trim the epoch from the currently-installed version if
|
||||
# no epoch is specified in the specified version
|
||||
norm_epoch = lambda x, y: x.split(':', 1)[-1] \
|
||||
if ':' not in y \
|
||||
else x
|
||||
cver = old_as_list.get(pkgname, [])
|
||||
if reinstall and cver:
|
||||
for ver in cver:
|
||||
ver = norm_epoch(ver, version_num)
|
||||
if salt.utils.compare_versions(ver1=version_num,
|
||||
oper='==',
|
||||
ver2=ver,
|
||||
cmp_func=version_cmp):
|
||||
# This version is already installed, so we need to
|
||||
# reinstall.
|
||||
to_reinstall[pkgname] = pkgstr
|
||||
break
|
||||
else:
|
||||
downgrade.append(pkgstr)
|
||||
if not cver:
|
||||
targets.append(pkgstr)
|
||||
else:
|
||||
for ver in cver:
|
||||
ver = norm_epoch(ver, version_num)
|
||||
if salt.utils.compare_versions(ver1=version_num,
|
||||
oper='>=',
|
||||
ver2=ver,
|
||||
cmp_func=version_cmp):
|
||||
targets.append(pkgstr)
|
||||
break
|
||||
else:
|
||||
if re.match('kernel(-.+)?', name):
|
||||
# kernel and its subpackages support multiple
|
||||
# installs as their paths do not conflict.
|
||||
# Performing a yum/dnf downgrade will be a no-op
|
||||
# so just do an install instead. It will fail if
|
||||
# there are other interdependencies that have
|
||||
# conflicts, and that's OK. We don't want to force
|
||||
# anything, we just want to properly handle it if
|
||||
# someone tries to install a kernel/kernel-devel of
|
||||
# a lower version than the currently-installed one.
|
||||
# TODO: find a better way to determine if a package
|
||||
# supports multiple installs.
|
||||
targets.append(pkgstr)
|
||||
else:
|
||||
# None of the currently-installed versions are
|
||||
# greater than the specified version, so this is a
|
||||
# downgrade.
|
||||
downgrade.append(pkgstr)
|
||||
|
||||
def _add_common_args(cmd):
|
||||
'''
|
||||
|
@ -1167,7 +1204,7 @@ def install(name=None,
|
|||
errors.append(out['stdout'])
|
||||
|
||||
__context__.pop('pkg.list_pkgs', None)
|
||||
new = list_pkgs()
|
||||
new = list_pkgs(versions_as_list=False)
|
||||
|
||||
ret = salt.utils.compare_dicts(old, new)
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ class SaltClientsMixIn(object):
|
|||
# not the actual client we'll use.. but its what we'll use to get args
|
||||
'local_batch': local_client.cmd_batch,
|
||||
'local_async': local_client.run_job,
|
||||
'runner': salt.runner.RunnerClient(opts=self.application.opts).async,
|
||||
'runner': salt.runner.RunnerClient(opts=self.application.opts).cmd_async,
|
||||
'runner_async': None, # empty, since we use the same client as `runner`
|
||||
}
|
||||
return SaltClientsMixIn.__saltclients
|
||||
|
@ -895,8 +895,6 @@ class SaltAPIHandler(BaseSaltAPIHandler, SaltClientsMixIn): # pylint: disable=W
|
|||
def disbatch(self):
|
||||
'''
|
||||
Disbatch all lowstates to the appropriate clients
|
||||
|
||||
Auth must have been verified before this point
|
||||
'''
|
||||
ret = []
|
||||
|
||||
|
@ -905,16 +903,23 @@ class SaltAPIHandler(BaseSaltAPIHandler, SaltClientsMixIn): # pylint: disable=W
|
|||
if not self._verify_client(low):
|
||||
return
|
||||
|
||||
for low in self.lowstate:
|
||||
# make sure that the chunk has a token, if not we can't do auth per-request
|
||||
# Note: this means that you can send different tokens per lowstate
|
||||
# as long as the base token (to auth with the API) is valid
|
||||
if 'token' not in low:
|
||||
# Make sure we have 'token' or 'username'/'password' in each low chunk.
|
||||
# Salt will verify the credentials are correct.
|
||||
if self.token is not None and 'token' not in low:
|
||||
low['token'] = self.token
|
||||
|
||||
if not (('token' in low)
|
||||
or ('username' in low and 'password' in low and 'eauth' in low)):
|
||||
ret.append('Failed to authenticate')
|
||||
break
|
||||
|
||||
# disbatch to the correct handler
|
||||
try:
|
||||
chunk_ret = yield getattr(self, '_disbatch_{0}'.format(low['client']))(low)
|
||||
ret.append(chunk_ret)
|
||||
except EauthAuthenticationError as exc:
|
||||
ret.append('Failed to authenticate')
|
||||
break
|
||||
except Exception as ex:
|
||||
ret.append('Unexpected exception while handling request: {0}'.format(ex))
|
||||
logger.error('Unexpected exception while handling request:', exc_info=True)
|
||||
|
@ -1112,8 +1117,7 @@ class SaltAPIHandler(BaseSaltAPIHandler, SaltClientsMixIn): # pylint: disable=W
|
|||
'''
|
||||
Disbatch runner client commands
|
||||
'''
|
||||
f_call = {'args': [chunk['fun'], chunk]}
|
||||
pub_data = self.saltclients['runner'](chunk['fun'], chunk)
|
||||
pub_data = self.saltclients['runner'](chunk)
|
||||
tag = pub_data['tag'] + '/ret'
|
||||
try:
|
||||
event = yield self.application.event_listener.get_event(self, tag=tag)
|
||||
|
@ -1128,8 +1132,7 @@ class SaltAPIHandler(BaseSaltAPIHandler, SaltClientsMixIn): # pylint: disable=W
|
|||
'''
|
||||
Disbatch runner client_async commands
|
||||
'''
|
||||
f_call = {'args': [chunk['fun'], chunk]}
|
||||
pub_data = self.saltclients['runner'](chunk['fun'], chunk)
|
||||
pub_data = self.saltclients['runner'](chunk)
|
||||
raise tornado.gen.Return(pub_data)
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ def get_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None,
|
|||
'local': Pillar
|
||||
}.get(opts['file_client'], Pillar)
|
||||
# If local pillar and we're caching, run through the cache system first
|
||||
log.info('Determining pillar cache')
|
||||
log.debug('Determining pillar cache')
|
||||
if opts['pillar_cache']:
|
||||
log.info('Compiling pillar from cache')
|
||||
log.debug('get_pillar using pillar cache with ext: {0}'.format(ext))
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
JSON5 Renderer for Salt
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
|
||||
JSON5 is an unofficial extension to JSON. See http://json5.org/ for more
|
||||
information.
|
||||
|
||||
This renderer requires the `json5 python bindings`__, installable via pip.
|
||||
|
||||
.. __: https://pypi.python.org/pypi/json5
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
try:
|
||||
import json5 as json
|
||||
HAS_JSON5 = True
|
||||
except ImportError:
|
||||
HAS_JSON5 = False
|
||||
|
||||
# Import salt libs
|
||||
from salt.ext.six import string_types
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'json5'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if not HAS_JSON5:
|
||||
return (False, 'json5 module not installed')
|
||||
return __virtualname__
|
||||
|
||||
|
||||
def render(json_data, saltenv='base', sls='', **kws):
|
||||
'''
|
||||
Accepts JSON as a string or as a file object and runs it through the JSON
|
||||
parser.
|
||||
|
||||
:rtype: A Python data structure
|
||||
'''
|
||||
if not isinstance(json_data, string_types):
|
||||
json_data = json_data.read()
|
||||
|
||||
if json_data.startswith('#!'):
|
||||
json_data = json_data[(json_data.find('\n') + 1):]
|
||||
if not json_data.strip():
|
||||
return {}
|
||||
return json.loads(json_data)
|
|
@ -393,6 +393,8 @@ class SPMClient(object):
|
|||
|
||||
can_has = {}
|
||||
cant_has = []
|
||||
if 'dependencies' in formula_def and formula_def['dependencies'] is None:
|
||||
formula_def['dependencies'] = ''
|
||||
for dep in formula_def.get('dependencies', '').split(','):
|
||||
dep = dep.strip()
|
||||
if not dep:
|
||||
|
|
|
@ -16,10 +16,10 @@ from contextlib import closing
|
|||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
|
||||
# # Use salt.utils.fopen
|
||||
# Import salt libs
|
||||
from salt.exceptions import CommandExecutionError
|
||||
import salt.utils
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
__virtualname__ = 'archive'
|
||||
|
@ -239,14 +239,31 @@ def extracted(name,
|
|||
__env__,
|
||||
'{0}.{1}'.format(re.sub('[:/\\\\]', '_', if_missing),
|
||||
archive_format))
|
||||
|
||||
if __opts__['test']:
|
||||
source_match = source
|
||||
else:
|
||||
try:
|
||||
source_match = __salt__['file.source_list'](source,
|
||||
source_hash,
|
||||
__env__)[0]
|
||||
except CommandExecutionError as exc:
|
||||
ret['result'] = False
|
||||
ret['comment'] = exc.strerror
|
||||
return ret
|
||||
|
||||
if not os.path.exists(filename):
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = \
|
||||
'Archive {0} would have been downloaded in cache'.format(source)
|
||||
'{0} {1} would be downloaded to cache'.format(
|
||||
'One of' if not isinstance(source_match, six.string_types)
|
||||
else 'Archive',
|
||||
source_match
|
||||
)
|
||||
return ret
|
||||
|
||||
log.debug('Archive file {0} is not in cache, download it'.format(source))
|
||||
log.debug('%s is not in cache, downloading it', source_match)
|
||||
file_result = __salt__['state.single']('file.managed',
|
||||
filename,
|
||||
source=source,
|
||||
|
@ -269,17 +286,21 @@ def extracted(name,
|
|||
log.debug('failed to download {0}'.format(source))
|
||||
return file_result
|
||||
else:
|
||||
log.debug('Archive file {0} is already in cache'.format(name))
|
||||
log.debug('Archive %s is already in cache', name)
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'Archive {0} would have been extracted in {1}'.format(
|
||||
source, name)
|
||||
ret['comment'] = '{0} {1} would be extracted to {2}'.format(
|
||||
'One of' if not isinstance(source_match, six.string_types)
|
||||
else 'Archive',
|
||||
source_match,
|
||||
name
|
||||
)
|
||||
return ret
|
||||
|
||||
__salt__['file.makedirs'](name, user=user, group=group)
|
||||
|
||||
log.debug('Extract {0} in {1}'.format(filename, name))
|
||||
log.debug('Extracting {0} to {1}'.format(filename, name))
|
||||
if archive_format == 'zip':
|
||||
files = __salt__['archive.unzip'](filename, name, options=zip_options, trim_output=trim_output, password=password)
|
||||
elif archive_format == 'rar':
|
||||
|
@ -327,7 +348,7 @@ def extracted(name,
|
|||
# Note: We do this here because we might not have access to the cachedir.
|
||||
if user or group:
|
||||
dir_result = __salt__['state.single']('file.directory',
|
||||
name,
|
||||
if_missing,
|
||||
user=user,
|
||||
group=group,
|
||||
recurse=['user', 'group'])
|
||||
|
@ -337,7 +358,7 @@ def extracted(name,
|
|||
ret['result'] = True
|
||||
ret['changes']['directories_created'] = [name]
|
||||
ret['changes']['extracted_files'] = files
|
||||
ret['comment'] = '{0} extracted in {1}'.format(source, name)
|
||||
ret['comment'] = '{0} extracted to {1}'.format(source_match, name)
|
||||
if not keep:
|
||||
os.unlink(filename)
|
||||
if source_hash and source_hash_update:
|
||||
|
@ -346,5 +367,5 @@ def extracted(name,
|
|||
else:
|
||||
__salt__['file.remove'](if_missing)
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Can\'t extract content of {0}'.format(source)
|
||||
ret['comment'] = 'Can\'t extract content of {0}'.format(source_match)
|
||||
return ret
|
||||
|
|
|
@ -329,7 +329,7 @@ def mounted(name,
|
|||
# convert uid/gid to numeric value from user/group name
|
||||
name_id_opts = {'uid': 'user.info',
|
||||
'gid': 'group.info'}
|
||||
if opt.split('=')[0] in name_id_opts:
|
||||
if opt.split('=')[0] in name_id_opts and len(opt.split('=')) > 1:
|
||||
_givenid = opt.split('=')[1]
|
||||
_param = opt.split('=')[0]
|
||||
_id = _givenid
|
||||
|
|
|
@ -122,6 +122,11 @@ def state(
|
|||
queue
|
||||
Pass ``queue=true`` through to the state function
|
||||
|
||||
batch
|
||||
Execute the command :ref:`in batches <targeting-batch>`. E.g.: ``10%``.
|
||||
|
||||
.. versionadded:: 2016.3.0
|
||||
|
||||
Examples:
|
||||
|
||||
Run a list of sls files via :py:func:`state.sls <salt.state.sls>` on target
|
||||
|
|
|
@ -31,7 +31,7 @@ import logging
|
|||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.utils.locales import sdecode
|
||||
from salt.utils.locales import sdecode, sdecode_if_string
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext.six import string_types, iteritems
|
||||
|
@ -149,10 +149,8 @@ def _changes(name,
|
|||
change['expire'] = expire
|
||||
|
||||
# GECOS fields
|
||||
if isinstance(fullname, string_types):
|
||||
fullname = sdecode(fullname)
|
||||
if isinstance(lusr['fullname'], string_types):
|
||||
lusr['fullname'] = sdecode(lusr['fullname'])
|
||||
fullname = sdecode_if_string(fullname)
|
||||
lusr['fullname'] = sdecode_if_string(lusr['fullname'])
|
||||
if fullname is not None and lusr['fullname'] != fullname:
|
||||
change['fullname'] = fullname
|
||||
if win_homedrive and lusr['homedrive'] != win_homedrive:
|
||||
|
@ -167,17 +165,23 @@ def _changes(name,
|
|||
# MacOS doesn't have full GECOS support, so check for the "ch" functions
|
||||
# and ignore these parameters if these functions do not exist.
|
||||
if 'user.chroomnumber' in __salt__ \
|
||||
and roomnumber is not None \
|
||||
and lusr['roomnumber'] != roomnumber:
|
||||
change['roomnumber'] = roomnumber
|
||||
and roomnumber is not None:
|
||||
roomnumber = sdecode_if_string(roomnumber)
|
||||
lusr['roomnumber'] = sdecode_if_string(lusr['roomnumber'])
|
||||
if lusr['roomnumber'] != roomnumber:
|
||||
change['roomnumber'] = roomnumber
|
||||
if 'user.chworkphone' in __salt__ \
|
||||
and workphone is not None \
|
||||
and lusr['workphone'] != workphone:
|
||||
change['workphone'] = workphone
|
||||
and workphone is not None:
|
||||
workphone = sdecode_if_string(workphone)
|
||||
lusr['workphone'] = sdecode_if_string(lusr['workphone'])
|
||||
if lusr['workphone'] != workphone:
|
||||
change['workphone'] = workphone
|
||||
if 'user.chhomephone' in __salt__ \
|
||||
and homephone is not None \
|
||||
and lusr['homephone'] != homephone:
|
||||
change['homephone'] = homephone
|
||||
and homephone is not None:
|
||||
homephone = sdecode_if_string(homephone)
|
||||
lusr['homephone'] = sdecode_if_string(lusr['homephone'])
|
||||
if lusr['homephone'] != homephone:
|
||||
change['homephone'] = homephone
|
||||
# OpenBSD/FreeBSD login class
|
||||
if __grains__['kernel'] in ('OpenBSD', 'FreeBSD'):
|
||||
if loginclass:
|
||||
|
@ -473,7 +477,7 @@ def present(name,
|
|||
for key, val in iteritems(changes):
|
||||
if key == 'password':
|
||||
val = 'XXX-REDACTED-XXX'
|
||||
ret['comment'] += '{0}: {1}\n'.format(key, val)
|
||||
ret['comment'] += u'{0}: {1}\n'.format(key, val)
|
||||
return ret
|
||||
# The user is present
|
||||
if 'shadow.info' in __salt__:
|
||||
|
|
|
@ -14,6 +14,8 @@ import os
|
|||
import salt.version
|
||||
import salt.utils
|
||||
|
||||
from salt.exceptions import CommandNotFoundError
|
||||
|
||||
from salt.ext import six
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -160,19 +162,24 @@ def managed(name,
|
|||
return ret
|
||||
|
||||
if not venv_exists or (venv_exists and clear):
|
||||
_ret = __salt__['virtualenv.create'](
|
||||
name,
|
||||
venv_bin=venv_bin,
|
||||
system_site_packages=system_site_packages,
|
||||
distribute=distribute,
|
||||
clear=clear,
|
||||
python=python,
|
||||
extra_search_dir=extra_search_dir,
|
||||
never_download=never_download,
|
||||
prompt=prompt,
|
||||
user=user,
|
||||
use_vt=use_vt,
|
||||
)
|
||||
try:
|
||||
_ret = __salt__['virtualenv.create'](
|
||||
name,
|
||||
venv_bin=venv_bin,
|
||||
system_site_packages=system_site_packages,
|
||||
distribute=distribute,
|
||||
clear=clear,
|
||||
python=python,
|
||||
extra_search_dir=extra_search_dir,
|
||||
never_download=never_download,
|
||||
prompt=prompt,
|
||||
user=user,
|
||||
use_vt=use_vt,
|
||||
)
|
||||
except CommandNotFoundError as err:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to create virtualenv: {0}'.formar(err)
|
||||
return ret
|
||||
|
||||
if _ret['retcode'] != 0:
|
||||
ret['result'] = False
|
||||
|
|
|
@ -801,7 +801,7 @@ def check_or_die(command):
|
|||
raise CommandNotFoundError('\'None\' is not a valid command.')
|
||||
|
||||
if not which(command):
|
||||
raise CommandNotFoundError(command)
|
||||
raise CommandNotFoundError('\'{0}\' is not in the path'.format(command))
|
||||
|
||||
|
||||
def backup_minion(path, bkroot):
|
||||
|
|
|
@ -2671,13 +2671,11 @@ def update_bootstrap(config, url=None):
|
|||
'''
|
||||
Update the salt-bootstrap script
|
||||
|
||||
url can be either:
|
||||
|
||||
- The URL to fetch the bootstrap script from
|
||||
- The absolute path to the bootstrap
|
||||
- The content of the bootstrap script
|
||||
|
||||
url can be one of:
|
||||
|
||||
- The URL to fetch the bootstrap script from
|
||||
- The absolute path to the bootstrap
|
||||
- The content of the bootstrap script
|
||||
'''
|
||||
default_url = config.get('bootstrap_script_url',
|
||||
'https://bootstrap.saltstack.com')
|
||||
|
|
|
@ -19,6 +19,18 @@ import subprocess
|
|||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.itertools
|
||||
import salt.utils.url
|
||||
import salt.fileserver
|
||||
from salt.utils.process import os_is_running as pid_exists
|
||||
from salt.exceptions import FileserverConfigError, GitLockError, get_error_message
|
||||
from salt.utils.event import tagify
|
||||
|
||||
# Import third party libs
|
||||
import salt.ext.six as six
|
||||
|
||||
VALID_PROVIDERS = ('gitpython', 'pygit2', 'dulwich')
|
||||
# Optional per-remote params that can only be used on a per-remote basis, and
|
||||
# thus do not have defaults in salt/config.py.
|
||||
|
@ -54,17 +66,8 @@ _INVALID_REPO = (
|
|||
'master to continue to use this {1} remote.'
|
||||
)
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
import salt.utils.itertools
|
||||
import salt.utils.url
|
||||
import salt.fileserver
|
||||
from salt.utils.process import os_is_running as pid_exists
|
||||
from salt.exceptions import FileserverConfigError, GitLockError
|
||||
from salt.utils.event import tagify
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Import third party libs
|
||||
import salt.ext.six as six
|
||||
# pylint: disable=import-error
|
||||
try:
|
||||
import git
|
||||
|
@ -80,8 +83,13 @@ try:
|
|||
GitError = pygit2.errors.GitError
|
||||
except AttributeError:
|
||||
GitError = Exception
|
||||
except ImportError:
|
||||
HAS_PYGIT2 = False
|
||||
except Exception as err: # cffi VerificationError also may happen
|
||||
HAS_PYGIT2 = False # and pygit2 requrests re-compilation
|
||||
# on a production system (!),
|
||||
# but cffi might be absent as well!
|
||||
# Therefore just a generic Exception class.
|
||||
if not isinstance(err, ImportError):
|
||||
log.error('Import pygit2 failed: {0}'.format(err))
|
||||
|
||||
try:
|
||||
import dulwich.errors
|
||||
|
@ -94,8 +102,6 @@ except ImportError:
|
|||
HAS_DULWICH = False
|
||||
# pylint: enable=import-error
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Minimum versions for backend providers
|
||||
GITPYTHON_MINVER = '0.3'
|
||||
PYGIT2_MINVER = '0.20.3'
|
||||
|
@ -1289,9 +1295,7 @@ class Pygit2(GitProvider):
|
|||
try:
|
||||
fetch_results = origin.fetch(**fetch_kwargs)
|
||||
except GitError as exc:
|
||||
# Using exc.__str__() here to avoid deprecation warning
|
||||
# when referencing exc.message
|
||||
exc_str = exc.__str__().lower()
|
||||
exc_str = get_error_message(exc).lower()
|
||||
if 'unsupported url protocol' in exc_str \
|
||||
and isinstance(self.credentials, pygit2.Keypair):
|
||||
log.error(
|
||||
|
|
|
@ -9,6 +9,7 @@ import sys
|
|||
|
||||
import salt.utils
|
||||
from salt.utils.decorators import memoize as real_memoize
|
||||
from salt.ext.six import string_types
|
||||
|
||||
|
||||
@real_memoize
|
||||
|
@ -50,6 +51,16 @@ def sdecode(string_):
|
|||
return string_
|
||||
|
||||
|
||||
def sdecode_if_string(value_):
|
||||
'''
|
||||
If the value is a string, run sdecode() on it to ensure it is parsed
|
||||
properly. If it is not a string, return it as-is
|
||||
'''
|
||||
if isinstance(value_, string_types):
|
||||
value_ = sdecode(value_)
|
||||
return value_
|
||||
|
||||
|
||||
def split_locale(loc):
|
||||
'''
|
||||
Split a locale specifier. The general format is
|
||||
|
|
|
@ -1393,8 +1393,7 @@ class ExecutionOptionsMixIn(six.with_metaclass(MixInMeta, object)):
|
|||
'-u', '--update-bootstrap',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Update salt-bootstrap to the latest develop version on '
|
||||
'GitHub.'
|
||||
help='Update salt-bootstrap to the latest stable bootstrap release.'
|
||||
)
|
||||
group.add_option(
|
||||
'-y', '--assume-yes',
|
||||
|
|
|
@ -174,6 +174,7 @@ class FileModuleTest(integration.ModuleCase):
|
|||
return_value=['http/httpd.conf.fallback']),
|
||||
'cp.list_master_dirs': MagicMock(return_value=[]),
|
||||
}
|
||||
filemod.__context__ = {}
|
||||
|
||||
ret = filemod.source_list(['salt://http/httpd.conf',
|
||||
'salt://http/httpd.conf.fallback'],
|
||||
|
@ -189,6 +190,8 @@ class FileModuleTest(integration.ModuleCase):
|
|||
'cp.list_master': MagicMock(side_effect=list_master),
|
||||
'cp.list_master_dirs': MagicMock(return_value=[]),
|
||||
}
|
||||
filemod.__context__ = {}
|
||||
|
||||
ret = filemod.source_list(['salt://http/httpd.conf?saltenv=dev',
|
||||
'salt://http/httpd.conf.fallback'],
|
||||
'filehash', 'base')
|
||||
|
@ -200,6 +203,8 @@ class FileModuleTest(integration.ModuleCase):
|
|||
'cp.list_master': MagicMock(return_value=['http/httpd.conf']),
|
||||
'cp.list_master_dirs': MagicMock(return_value=[]),
|
||||
}
|
||||
filemod.__context__ = {}
|
||||
|
||||
ret = filemod.source_list(
|
||||
[{'salt://http/httpd.conf': ''}], 'filehash', 'base')
|
||||
self.assertItemsEqual(ret, ['salt://http/httpd.conf', 'filehash'])
|
||||
|
@ -210,8 +215,10 @@ class FileModuleTest(integration.ModuleCase):
|
|||
filemod.__salt__ = {
|
||||
'cp.list_master': MagicMock(return_value=[]),
|
||||
'cp.list_master_dirs': MagicMock(return_value=[]),
|
||||
'cp.get_url': MagicMock(return_value='/tmp/http.conf'),
|
||||
'cp.cache_file': MagicMock(return_value='/tmp/http.conf'),
|
||||
}
|
||||
filemod.__context__ = {}
|
||||
|
||||
ret = filemod.source_list(
|
||||
[{'http://t.est.com/http/httpd.conf': 'filehash'}], '', 'base')
|
||||
self.assertItemsEqual(ret, ['http://t.est.com/http/httpd.conf',
|
||||
|
|
|
@ -17,6 +17,7 @@ ensure_in_syspath('../../../')
|
|||
from tests.utils import BaseRestCherryPyTest
|
||||
|
||||
# Import Salt Libs
|
||||
import salt.utils
|
||||
from tests import integration
|
||||
|
||||
# Import 3rd-party libs
|
||||
|
@ -31,7 +32,7 @@ except ImportError:
|
|||
|
||||
USERA = 'saltdev'
|
||||
USERA_PWD = 'saltdev'
|
||||
SET_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.'
|
||||
HASHED_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.'
|
||||
|
||||
AUTH_CREDS = {
|
||||
'username': USERA,
|
||||
|
@ -52,7 +53,7 @@ class TestAuthPAM(BaseRestCherryPyTest, integration.ModuleCase):
|
|||
try:
|
||||
add_user = self.run_function('user.add', [USERA], createhome=False)
|
||||
add_pwd = self.run_function('shadow.set_password',
|
||||
[USERA, SET_USERA_PWD])
|
||||
[USERA, USERA_PWD if salt.utils.is_darwin() else HASHED_USERA_PWD])
|
||||
self.assertTrue(add_user)
|
||||
self.assertTrue(add_pwd)
|
||||
user_list = self.run_function('user.list_users')
|
||||
|
|
|
@ -19,6 +19,7 @@ from salttesting.helpers import (
|
|||
ensure_in_syspath('../../')
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.utils.pycrypto import gen_hash
|
||||
import integration
|
||||
|
||||
|
@ -76,11 +77,18 @@ class AuthTest(integration.ShellCase):
|
|||
|
||||
def test_pam_auth_valid_user(self):
|
||||
'''
|
||||
test pam auth mechanism is working with a valid user
|
||||
test that pam auth mechanism works with a valid user
|
||||
'''
|
||||
password, hashed_pwd = gen_password()
|
||||
self.run_call("shadow.set_password {0} '{1}'".format(self.userA, hashed_pwd))
|
||||
|
||||
# set user password
|
||||
set_pw_cmd = "shadow.set_password {0} '{1}'".format(
|
||||
self.userA,
|
||||
password if salt.utils.is_darwin() else hashed_pwd
|
||||
)
|
||||
self.run_call(set_pw_cmd)
|
||||
|
||||
# test user auth against pam
|
||||
cmd = ('-a pam "*" test.ping '
|
||||
'--username {0} --password {1}'.format(self.userA, password))
|
||||
resp = self.run_salt(cmd)
|
||||
|
@ -101,11 +109,19 @@ class AuthTest(integration.ShellCase):
|
|||
|
||||
def test_pam_auth_valid_group(self):
|
||||
'''
|
||||
test pam auth mechanism success for a valid group
|
||||
test that pam auth mechanism works for a valid group
|
||||
'''
|
||||
password, hashed_pwd = gen_password()
|
||||
self.run_call("shadow.set_password {0} '{1}'".format(self.userB, hashed_pwd))
|
||||
|
||||
# set user password
|
||||
set_pw_cmd = "shadow.set_password {0} '{1}'".format(
|
||||
self.userB,
|
||||
password if salt.utils.is_darwin() else hashed_pwd
|
||||
)
|
||||
self.run_call(set_pw_cmd)
|
||||
|
||||
# test group auth against pam: saltadm is not configured in
|
||||
# external_auth, but saltops is and saldadm is a member of saltops
|
||||
cmd = ('-a pam "*" test.ping '
|
||||
'--username {0} --password {1}'.format(self.userB, password))
|
||||
resp = self.run_salt(cmd)
|
||||
|
|
|
@ -33,7 +33,7 @@ class EnabledTest(integration.ModuleCase):
|
|||
'''
|
||||
enabled_ret = '3\nsaltines' # the result of running self.cmd in a shell
|
||||
ret = self.run_function('cmd.run', [self.cmd])
|
||||
self.assertEqual(ret, enabled_ret)
|
||||
self.assertEqual(ret.strip(), enabled_ret)
|
||||
|
||||
def test_shell_disabled(self):
|
||||
'''
|
||||
|
|
|
@ -156,6 +156,29 @@ class UserTest(integration.ModuleCase,
|
|||
ret = self.run_state('group.absent', name='salt_test')
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'you must be root to run this test')
|
||||
def test_user_present_unicode(self):
|
||||
'''
|
||||
This is a DESTRUCTIVE TEST it creates a new user on the on the minion.
|
||||
|
||||
It ensures that unicode GECOS data will be properly handled, without
|
||||
any encoding-related failures.
|
||||
'''
|
||||
ret = self.run_state(
|
||||
'user.present', name='salt_test', fullname=u'Sålt Test', roomnumber=u'①②③',
|
||||
workphone=u'١٢٣٤', homephone=u'६७८'
|
||||
)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
# Ensure updating a user also works
|
||||
ret = self.run_state(
|
||||
'user.present', name='salt_test', fullname=u'Sølt Test', roomnumber=u'①③②',
|
||||
workphone=u'٣٤١٢', homephone=u'६८७'
|
||||
)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
ret = self.run_state('user.absent', name='salt_test')
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(os.geteuid() != 0, 'you must be root to run this test')
|
||||
def test_user_present_gecos(self):
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: `Anthony Shaw <anthonyshaw@apache.org>`
|
||||
|
||||
tests.unit.cloud.clouds.dimensiondata_test
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.cloud.clouds import dimensiondata
|
||||
from salt.exceptions import SaltCloudSystemExit
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import TestCase, skipIf
|
||||
from salttesting.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../../')
|
||||
|
||||
# Global Variables
|
||||
dimensiondata.__active_provider_name__ = ''
|
||||
dimensiondata.__opts__ = {
|
||||
'providers': {
|
||||
'my-dimensiondata-cloud': {
|
||||
'dimensiondata': {
|
||||
'driver': 'dimensiondata',
|
||||
'region': 'dd-au',
|
||||
'user_id': 'jon_snow',
|
||||
'key': 'IKnowNothing'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VM_NAME = 'winterfell'
|
||||
|
||||
|
||||
class ExtendedTestCase(TestCase):
|
||||
'''
|
||||
Extended TestCase class containing additional helper methods.
|
||||
'''
|
||||
|
||||
def assertRaisesWithMessage(self, exc_type, exc_msg, func, *args, **kwargs):
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
self.assertFail()
|
||||
except Exception as exc:
|
||||
self.assertEqual(type(exc), exc_type)
|
||||
self.assertEqual(exc.message, exc_msg)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@patch('salt.cloud.clouds.dimensiondata.__virtual__', MagicMock(return_value='dimensiondata'))
|
||||
class DimensionDataTestCase(ExtendedTestCase):
|
||||
'''
|
||||
Unit TestCase for salt.cloud.clouds.dimensiondata module.
|
||||
'''
|
||||
|
||||
def test_avail_images_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call avail_images
|
||||
with --action or -a.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
dimensiondata.avail_images,
|
||||
call='action'
|
||||
)
|
||||
|
||||
def test_avail_locations_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call avail_locations
|
||||
with --action or -a.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
dimensiondata.avail_locations,
|
||||
call='action'
|
||||
)
|
||||
|
||||
def test_avail_sizes_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call avail_sizes
|
||||
with --action or -a.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
dimensiondata.avail_sizes,
|
||||
call='action'
|
||||
)
|
||||
|
||||
def test_list_nodes_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call list_nodes
|
||||
with --action or -a.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
dimensiondata.list_nodes,
|
||||
call='action'
|
||||
)
|
||||
|
||||
def test_destroy_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call destroy
|
||||
with --function or -f.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
dimensiondata.destroy,
|
||||
name=VM_NAME,
|
||||
call='function'
|
||||
)
|
||||
|
||||
def test_avail_sizes(self):
|
||||
'''
|
||||
Tests that avail_sizes returns an empty dictionary.
|
||||
'''
|
||||
sizes = dimensiondata.avail_sizes(call='foo')
|
||||
self.assertEqual(
|
||||
len(sizes),
|
||||
1
|
||||
)
|
||||
self.assertEqual(
|
||||
sizes['default']['name'],
|
||||
'default'
|
||||
)
|
||||
|
||||
@patch('libcloud.compute.drivers.dimensiondata.DimensionDataNodeDriver.list_nodes', MagicMock(return_value=[]))
|
||||
def test_list_nodes(self):
|
||||
nodes = dimensiondata.list_nodes()
|
||||
self.assertEqual(
|
||||
nodes,
|
||||
{}
|
||||
)
|
||||
|
||||
@patch('libcloud.compute.drivers.dimensiondata.DimensionDataNodeDriver.list_locations', MagicMock(return_value=[]))
|
||||
def test_list_locations(self):
|
||||
locations = dimensiondata.avail_locations()
|
||||
self.assertEqual(
|
||||
locations,
|
||||
{}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(DimensionDataTestCase, needs_daemon=False)
|
|
@ -0,0 +1,113 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: `Anthony Shaw <anthonyshaw@apache.org>`
|
||||
|
||||
tests.unit.cloud.clouds.gce_test
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.cloud.clouds import gce
|
||||
from salt.exceptions import SaltCloudSystemExit
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import TestCase, skipIf
|
||||
from salttesting.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../../')
|
||||
|
||||
# Global Variables
|
||||
gce.__active_provider_name__ = ''
|
||||
gce.__opts__ = {
|
||||
'providers': {
|
||||
'my-google-cloud': {
|
||||
'gce': {
|
||||
'project': 'daenerys-cloud',
|
||||
'service_account_email_address': 'dany@targaryen.westeros.cloud',
|
||||
'service_account_private_key': '/home/dany/PRIVKEY.pem',
|
||||
'driver': 'gce',
|
||||
'ssh_interface': 'public_ips'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VM_NAME = 'kings_landing'
|
||||
DUMMY_TOKEN = {
|
||||
'refresh_token': None,
|
||||
'client_id': 'dany123',
|
||||
'client_secret': 'lalalalalalala',
|
||||
'grant_type': 'refresh_token'
|
||||
}
|
||||
|
||||
|
||||
class ExtendedTestCase(TestCase):
|
||||
'''
|
||||
Extended TestCase class containing additional helper methods.
|
||||
'''
|
||||
|
||||
def assertRaisesWithMessage(self, exc_type, exc_msg, func, *args, **kwargs):
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
self.assertFail()
|
||||
except Exception as exc:
|
||||
self.assertEqual(type(exc), exc_type)
|
||||
self.assertEqual(exc.message, exc_msg)
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@patch('salt.cloud.clouds.gce.__virtual__', MagicMock(return_value='gce'))
|
||||
@patch('libcloud.common.google.GoogleInstalledAppAuthConnection.get_new_token', MagicMock(return_value=DUMMY_TOKEN))
|
||||
@patch('libcloud.compute.drivers.gce.GCENodeDriver.ex_list_zones', MagicMock(return_value=[]))
|
||||
@patch('libcloud.compute.drivers.gce.GCENodeDriver.ex_list_regions', MagicMock(return_value=[]))
|
||||
class GCETestCase(ExtendedTestCase):
|
||||
'''
|
||||
Unit TestCase for salt.cloud.clouds.gce module.
|
||||
'''
|
||||
|
||||
def test_destroy_call(self):
|
||||
'''
|
||||
Tests that a SaltCloudSystemExit is raised when trying to call destroy
|
||||
with --function or -f.
|
||||
'''
|
||||
self.assertRaises(
|
||||
SaltCloudSystemExit,
|
||||
gce.destroy,
|
||||
vm_name=VM_NAME,
|
||||
call='function'
|
||||
)
|
||||
|
||||
@patch('libcloud.compute.drivers.gce.GCENodeDriver.list_sizes', MagicMock(return_value=[]))
|
||||
def test_avail_sizes(self):
|
||||
'''
|
||||
Tests that avail_sizes returns an empty dictionary.
|
||||
'''
|
||||
sizes = gce.avail_sizes()
|
||||
self.assertEqual(
|
||||
sizes,
|
||||
[]
|
||||
)
|
||||
|
||||
@patch('libcloud.compute.drivers.gce.GCENodeDriver.list_nodes', MagicMock(return_value=[]))
|
||||
def test_list_nodes(self):
|
||||
nodes = gce.list_nodes()
|
||||
self.assertEqual(
|
||||
nodes,
|
||||
{}
|
||||
)
|
||||
|
||||
@patch('libcloud.compute.drivers.gce.GCENodeDriver.list_locations', MagicMock(return_value=[]))
|
||||
def test_list_locations(self):
|
||||
locations = gce.avail_locations()
|
||||
self.assertEqual(
|
||||
locations,
|
||||
{}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(GCETestCase, needs_daemon=False)
|
|
@ -124,8 +124,9 @@ class DaemonsStarterTestCase(TestCase, integration.SaltClientTestCaseMixIn):
|
|||
'''
|
||||
obj = daemons.Minion()
|
||||
obj.config = {'user': 'dummy', 'hash_type': alg}
|
||||
for attr in ['minion', 'start_log_info', 'prepare', 'shutdown']:
|
||||
for attr in ['start_log_info', 'prepare', 'shutdown']:
|
||||
setattr(obj, attr, MagicMock())
|
||||
setattr(obj, 'minion', MagicMock(restart=False))
|
||||
|
||||
return obj
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
from distutils.version import LooseVersion # pylint: disable=import-error,no-name-in-module
|
||||
import platform
|
||||
import random
|
||||
import string
|
||||
|
||||
|
@ -35,6 +36,10 @@ try:
|
|||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
|
||||
ON_SUSE = False
|
||||
if 'SuSE' in platform.dist():
|
||||
ON_SUSE = True
|
||||
|
||||
# pylint: enable=import-error,no-name-in-module
|
||||
|
||||
# the boto_lambda module relies on the connect_to_region() method
|
||||
|
@ -619,6 +624,7 @@ class BotoLambdaEventSourceMappingTestCase(BotoLambdaTestCaseBase, BotoLambdaTes
|
|||
**conn_parameters)
|
||||
self.assertTrue(result['deleted'])
|
||||
|
||||
@skipIf(ON_SUSE, 'Skipping while debugging why the test suite hangs and bails on this test on opensuse')
|
||||
def test_that_when_deleting_an_event_source_mapping_by_name_succeeds_the_delete_event_source_mapping_method_returns_true(self):
|
||||
'''
|
||||
tests True mapping deleted.
|
||||
|
|
|
@ -178,6 +178,10 @@ class PwUserTestCase(TestCase):
|
|||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chfullname('name', 'fullname'))
|
||||
|
||||
mock = MagicMock(return_value={'fullname': u'Unicøde name ①③②'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chfullname('name', u'Unicøde name ①③②'))
|
||||
|
||||
mock = MagicMock(return_value={'fullname': 'fullname'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
mock = MagicMock(return_value=None)
|
||||
|
@ -202,6 +206,10 @@ class PwUserTestCase(TestCase):
|
|||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertFalse(pw_user.chroomnumber('name', 1))
|
||||
|
||||
mock = MagicMock(return_value={'roomnumber': u'Unicøde room ①③②'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chroomnumber('name', u'Unicøde room ①③②'))
|
||||
|
||||
mock = MagicMock(return_value={'roomnumber': '1'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chroomnumber('name', 1))
|
||||
|
@ -234,6 +242,10 @@ class PwUserTestCase(TestCase):
|
|||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chworkphone('name', 1))
|
||||
|
||||
mock = MagicMock(return_value={'workphone': u'Unicøde phone number ①③②'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chworkphone('name', u'Unicøde phone number ①③②'))
|
||||
|
||||
mock = MagicMock(return_value={'workphone': '2'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
mock = MagicMock(return_value=None)
|
||||
|
@ -262,6 +274,10 @@ class PwUserTestCase(TestCase):
|
|||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chhomephone('name', 1))
|
||||
|
||||
mock = MagicMock(return_value={'homephone': u'Unicøde phone number ①③②'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
self.assertTrue(pw_user.chhomephone('name', u'Unicøde phone number ①③②'))
|
||||
|
||||
mock = MagicMock(return_value={'homephone': '2'})
|
||||
with patch.object(pw_user, '_get_gecos', mock):
|
||||
mock = MagicMock(return_value=None)
|
||||
|
|
|
@ -69,7 +69,7 @@ class SaltnadoTestCase(integration.ModuleCase, AsyncHTTPTestCase):
|
|||
|
||||
@property
|
||||
def opts(self):
|
||||
return self.get_config('master', from_scratch=True)
|
||||
return self.get_config('client_config', from_scratch=True)
|
||||
|
||||
@property
|
||||
def mod_opts(self):
|
||||
|
|
|
@ -65,6 +65,7 @@ class ArchiveTestCase(TestCase):
|
|||
mock_false = MagicMock(return_value=False)
|
||||
ret = {'stdout': ['saltines', 'cheese'], 'stderr': 'biscuits', 'retcode': '31337', 'pid': '1337'}
|
||||
mock_run = MagicMock(return_value=ret)
|
||||
mock_source_list = MagicMock(return_value=source)
|
||||
|
||||
with patch('os.path.exists', mock_true):
|
||||
with patch.dict(archive.__opts__, {'test': False,
|
||||
|
@ -72,7 +73,8 @@ class ArchiveTestCase(TestCase):
|
|||
with patch.dict(archive.__salt__, {'file.directory_exists': mock_false,
|
||||
'file.file_exists': mock_false,
|
||||
'file.makedirs': mock_true,
|
||||
'cmd.run_all': mock_run}):
|
||||
'cmd.run_all': mock_run,
|
||||
'file.source_list': mock_source_list}):
|
||||
filename = os.path.join(
|
||||
tmp_dir,
|
||||
'files/test/_tmp_test_archive_.tar'
|
||||
|
@ -90,10 +92,19 @@ class ArchiveTestCase(TestCase):
|
|||
Tests the call of extraction with gnutar
|
||||
'''
|
||||
gnutar = MagicMock(return_value='tar (GNU tar)')
|
||||
source = 'GNU tar'
|
||||
missing = MagicMock(return_value=False)
|
||||
nop = MagicMock(return_value=True)
|
||||
run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'})
|
||||
with patch.dict(archive.__salt__, {'cmd.run': gnutar, 'file.directory_exists': missing, 'file.file_exists': missing, 'state.single': nop, 'file.makedirs': nop, 'cmd.run_all': run_all}):
|
||||
mock_source_list = MagicMock(return_value=source)
|
||||
|
||||
with patch.dict(archive.__salt__, {'cmd.run': gnutar,
|
||||
'file.directory_exists': missing,
|
||||
'file.file_exists': missing,
|
||||
'state.single': nop,
|
||||
'file.makedirs': nop,
|
||||
'cmd.run_all': run_all,
|
||||
'file.source_list': mock_source_list}):
|
||||
ret = archive.extracted('/tmp/out', '/tmp/foo.tar.gz', 'tar', tar_options='xvzf', keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stdout')
|
||||
|
||||
|
@ -102,10 +113,19 @@ class ArchiveTestCase(TestCase):
|
|||
Tests the call of extraction with bsdtar
|
||||
'''
|
||||
bsdtar = MagicMock(return_value='tar (bsdtar)')
|
||||
source = 'bsdtar'
|
||||
missing = MagicMock(return_value=False)
|
||||
nop = MagicMock(return_value=True)
|
||||
run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'})
|
||||
with patch.dict(archive.__salt__, {'cmd.run': bsdtar, 'file.directory_exists': missing, 'file.file_exists': missing, 'state.single': nop, 'file.makedirs': nop, 'cmd.run_all': run_all}):
|
||||
mock_source_list = MagicMock(return_value=source)
|
||||
|
||||
with patch.dict(archive.__salt__, {'cmd.run': bsdtar,
|
||||
'file.directory_exists': missing,
|
||||
'file.file_exists': missing,
|
||||
'state.single': nop,
|
||||
'file.makedirs': nop,
|
||||
'cmd.run_all': run_all,
|
||||
'file.source_list': mock_source_list}):
|
||||
ret = archive.extracted('/tmp/out', '/tmp/foo.tar.gz', 'tar', tar_options='xvzf', keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stderr')
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче