mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 17:09:03 +00:00
Merge branch 'develop' of github.com:saltstack/salt into cherry-pick/CLOEXEC
This commit is contained in:
commit
6ec7646d51
@ -5,7 +5,7 @@ python:
|
||||
- '2.7'
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update && sudo apt-get install swig supervisor rabbitmq-server
|
||||
- sudo apt-get update && sudo apt-get install swig supervisor rabbitmq-server ruby
|
||||
- pip install http://dl.dropbox.com/u/174789/m2crypto-0.20.1.tar.gz
|
||||
- "if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi"
|
||||
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
||||
salt (0.10.5) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Sean Channel <pentabular@gmail.com> Thu, 15 Nov 2012 23:51:50 -0700
|
||||
|
||||
salt (0.10.4-2) unstable; urgency=low
|
||||
|
||||
* zmq3 support: b84f593770 86af5f03c3 c933c9e4ff a7e1d58f87 a5cdd8e313 c10bf6702f 2ea8b7e061 75f2a58b4e
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-CALL" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-CALL" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-call \- salt-call Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
@ -109,5 +109,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-CP" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-CP" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-cp \- salt-cp Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.sp
|
||||
Copy a file to a set of systems
|
||||
@ -120,5 +120,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-KEY" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-KEY" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-key \- salt-key Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
@ -141,5 +141,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-MASTER" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-MASTER" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-master \- salt-master Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.sp
|
||||
The Salt master daemon, used to control the Salt minions
|
||||
@ -81,5 +81,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-MINION" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-MINION" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-minion \- salt-minion Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.sp
|
||||
The Salt minion daemon, receives commands from a remote Salt master.
|
||||
@ -82,5 +82,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-RUN" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-RUN" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-run \- salt-run Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.sp
|
||||
Execute a Salt runner
|
||||
@ -67,5 +67,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT-SYNDIC" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT-SYNDIC" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt-syndic \- salt-syndic Documentation
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.sp
|
||||
The Salt syndic daemon, a special minion that passes through commands from a
|
||||
@ -76,5 +76,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "SALT" "1" "October 23, 2012" "0.10.4" "Salt"
|
||||
.TH "SALT" "1" "November 15, 2012" "0.10.5" "Salt"
|
||||
.SH NAME
|
||||
salt \- salt
|
||||
.
|
||||
@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.\" Man page generated from reStructeredText.
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
.INDENT 0.0
|
||||
@ -227,5 +227,4 @@ Thomas S. Hatch <thatch45@gmail.com> and many others, please see the Authors fil
|
||||
.SH COPYRIGHT
|
||||
2012, Thomas S. Hatch
|
||||
.\" Generated by docutils manpage writer.
|
||||
.\"
|
||||
.
|
||||
|
5063
doc/man/salt.7
5063
doc/man/salt.7
File diff suppressed because it is too large
Load Diff
@ -49,18 +49,43 @@ This example would instruct all Salt minions to download the vimrc from a
|
||||
directory with the same name as their os grain and copy it to /etc/vimrc
|
||||
|
||||
For larger files, the cp.get_file module also supports gzip compression.
|
||||
Because gzip compression is CPU-intensive, this should only be used in
|
||||
Because gzip is CPU-intensive, this should only be used in
|
||||
scenarios where the compression ratio is very high (e.g. pretty-printed JSON
|
||||
or YAML files).
|
||||
|
||||
Use the *gzip_compression* named argument to enable it. Valid values are 1..9,
|
||||
Use the *gzip* named argument to enable it. Valid values are 1..9,
|
||||
where 1 is the lightest compression and 9 the heaviest. 1 uses the least CPU
|
||||
on the master (and minion), 9 uses the most.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt '*' cp.get_file salt://vimrc /etc/vimrc gzip_compression=5
|
||||
# salt '*' cp.get_file salt://vimrc /etc/vimrc gzip=5
|
||||
|
||||
Finally, note that by default cp.get_file does *not* create new destination
|
||||
directories if they do not exist. To change this, use the *makedirs* argument:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt '*' cp.get_file salt://vimrc /etc/vim/vimrc makedirs=True
|
||||
|
||||
In this example, /etc/vim/ would be created if it didn't already exist.
|
||||
|
||||
get_dir
|
||||
```````
|
||||
|
||||
The cp.get_dir function can be used on the minion to download an entire
|
||||
directory from the master. The syntax is very similar to get_file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt '*' cp.get_dir salt://etc/apache2 /etc
|
||||
|
||||
cp.get_dir supports *template* rendering and *gzip* compression arguments just
|
||||
like get_file:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# salt '*' cp.get_dir salt://etc/{{pillar.webserver}} /etc gzip=5 template=jinja
|
||||
|
||||
|
||||
File Server Client API
|
||||
|
@ -47,7 +47,7 @@ The first line is a shebang that references the ``py`` renderer.
|
||||
|
||||
Composing Renderers
|
||||
-------------------
|
||||
A render can be composed from other renderers by connecting them in a series
|
||||
A renderer can be composed from other renderers by connecting them in a series
|
||||
of pipes(``|``). In fact, the default ``Jinja + YAML`` renderer is implemented
|
||||
by combining a yaml renderer and a jinja renderer. Such renderer configuration
|
||||
is specified as: ``jinja | yaml``.
|
||||
@ -102,8 +102,9 @@ Here is a simple YAML renderer example:
|
||||
.. code-block:: python
|
||||
|
||||
import yaml
|
||||
def render(yaml_data, env='', sls='', argline='', **kws):
|
||||
def render(yaml_data, env='', sls='', **kws):
|
||||
if not isinstance(yaml_data, basestring):
|
||||
yaml_data = yaml_data.read()
|
||||
data = yaml.load(yaml_data)
|
||||
return data if data else {}
|
||||
|
||||
|
157
doc/topics/releases/0.10.5.rst
Normal file
157
doc/topics/releases/0.10.5.rst
Normal file
@ -0,0 +1,157 @@
|
||||
=========================
|
||||
Salt 0.10.5 Release Notes
|
||||
=========================
|
||||
|
||||
Salt 0.10.5 is ready, and comes with some great new features. A few more
|
||||
interfaces have been modularized, like the outputter system. The job cache
|
||||
system has been made more powerful and can now store and retrieve jobs archived
|
||||
in external databases. The returner system has been extended to allow minions
|
||||
to easily retrieve data from a returner interface.
|
||||
|
||||
As usual, this is an exciting release, with many noteworthy additions!
|
||||
|
||||
Major Features
|
||||
==============
|
||||
|
||||
External Job Cache
|
||||
------------------
|
||||
|
||||
The external job cache is a system which allows for a returner interface to
|
||||
also act as a job cache. This system is intended to allow users to store
|
||||
job information in a central location for longer periods of time and to make
|
||||
the act of looking up information from jobs executed on other minions easier.
|
||||
|
||||
Currently the external job cache is supported via the mongo and redis
|
||||
returners:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ext_job_cache: redis
|
||||
redis.host: salt
|
||||
|
||||
Once the external job cache is turned on the new `ret` module can be used on
|
||||
the minions to retrieve return information from the job cache. This can be a
|
||||
great way for minions to respond and react to other minions.
|
||||
|
||||
OpenStack Additions
|
||||
--------------------
|
||||
|
||||
OpenStack integration with Salt has been moving forward at a blistering pace.
|
||||
The new `nova`, `glance` and `keystone` modules represent the beginning of
|
||||
ongoing OpenStack integration.
|
||||
|
||||
The Salt team has had many conversations with core OpenStack developers and
|
||||
is working on linking to OpenStack in powerful new ways.
|
||||
|
||||
Wheel System
|
||||
------------
|
||||
|
||||
A new API was added to the Salt Master which allows the master to be managed
|
||||
via an external API. This new system allows Salt API to easily hook into the
|
||||
Salt Master and manage configs, modify the state tree, manage the pillar and
|
||||
more. The main motivation for the wheel system is to enable features needed
|
||||
in the upcoming web ui so users can manage the master just as easily as they
|
||||
manage minions.
|
||||
|
||||
The wheel system has also been hooked into the external auth system. This
|
||||
allows specific users to have granular access to manage components of the
|
||||
Salt Master.
|
||||
|
||||
Render Pipes
|
||||
------------
|
||||
|
||||
Jack Kuan has added a substantial new feature. The render pipes system allows
|
||||
Salt to treat the render system like unix pipes. This new system enables sls
|
||||
files to be passed through specific render engines. While the default renderer
|
||||
is still recommended, different engines can now be more easily merged. So to
|
||||
pipe the output of mako used in YAML use this shebang line:
|
||||
|
||||
#!mako|yaml
|
||||
|
||||
Salt Key Overhaul
|
||||
-----------------
|
||||
|
||||
The Salt Key system was originally developed as only a cli interface, but as
|
||||
time went on it was pressed into becoming a clumsy API. This release marks a
|
||||
complete overhaul of Salt Key. Salt Key has been rewritten to function purely
|
||||
from an api and to use the outputter system. The benefit here is that the
|
||||
outputter system works much more cleanly with Salt Key now, and the internals
|
||||
of Salt Key can be used much more cleanly.
|
||||
|
||||
Modular Outputters
|
||||
------------------
|
||||
|
||||
The outputter system is now loaded in a modular way. This means that output
|
||||
systems can be more easily added by dropping a python file down on the master
|
||||
that contains the function `output`.
|
||||
|
||||
Gzip from Fileserver
|
||||
--------------------
|
||||
|
||||
Gzip compression has been added as an option to the cp.get_file and cp.get_dir
|
||||
commands. This will make file transfers more efficient and faster, especially
|
||||
over slower network links.
|
||||
|
||||
Unified Module Configuration
|
||||
----------------------------
|
||||
|
||||
In past releases of Salt, the minions needed to be configured for certain
|
||||
modules to function. This was difficult because it required pre-configuring the
|
||||
minions. 0.10.5 changes this by making all module configs on minions search the
|
||||
master config file for values.
|
||||
|
||||
Now if a single database server is needed, then it can be defined in the master
|
||||
config and all minions will become aware of the configuration value.
|
||||
|
||||
Salt Call Enhancements
|
||||
----------------------
|
||||
|
||||
The ``salt-call`` command has been updated in a few ways. Now, ``salt-call``
|
||||
can take the --return option to send the data to a returner. Also,
|
||||
``salt-call`` now reports executions in the minion proc system, this allows the
|
||||
master to be aware of the operation salt-call is running.
|
||||
|
||||
Death to pub_refresh and sub_timeout
|
||||
------------------------------------
|
||||
|
||||
The old configuration values `pub_refresh` and `sub_timeout` have been removed.
|
||||
These options were in place to alleviate problems found in earlier versions of
|
||||
ZeroMQ which have since been fixed. The continued use of these options has
|
||||
proven to cause problems with message passing and have been completely removed.
|
||||
|
||||
Git Revision Versions
|
||||
---------------------
|
||||
|
||||
When running Salt directly from git (for testing or development, of course)
|
||||
it has been difficult to know exactly what code is being executed. The new
|
||||
versioning system will detect the git revision when building and how many
|
||||
commits have been made since the last release. A release from git will look
|
||||
like this:
|
||||
|
||||
0.10.4-736-gec74d69
|
||||
|
||||
Svn Module Addition
|
||||
-------------------
|
||||
|
||||
Anthony Cornehl (twinshadow) contributed a module that adds Subversion support
|
||||
to Salt. This great addition helps round out Salt's VCS support.
|
||||
|
||||
Noteworthy Changes
|
||||
==================
|
||||
|
||||
Arch Linux Defaults to Systemd
|
||||
------------------------------
|
||||
|
||||
Arch Linux recently changed to use systemd by default and discontinued support
|
||||
for init scripts. Salt has followed suit and defaults to systemd now for
|
||||
managing services in Arch.
|
||||
|
||||
Salt, Salt Cloud and Openstack
|
||||
------------------------------
|
||||
|
||||
With the releases of Salt 0.10.5 and Salt Cloud 0.8.2, OpenStack becomes the
|
||||
first (non-OS) piece of software to include support both on the user level
|
||||
(with Salt Cloud) and the admin level (with Salt). We are excited to continue
|
||||
to extend support of other platforms at this level.
|
||||
|
||||
|
@ -147,7 +147,7 @@ def prepend_root_dir(opts, path_options):
|
||||
opts[path_option])
|
||||
|
||||
|
||||
def minion_config(path):
|
||||
def minion_config(path, check_dns=True):
|
||||
'''
|
||||
Reads in the minion configuration file and sets up special options
|
||||
'''
|
||||
@ -230,24 +230,33 @@ def minion_config(path):
|
||||
if 'append_domain' in opts:
|
||||
opts['id'] = _append_domain(opts)
|
||||
|
||||
try:
|
||||
opts['master_ip'] = salt.utils.dns_check(opts['master'], True)
|
||||
except SaltClientError:
|
||||
if opts['retry_dns']:
|
||||
while True:
|
||||
msg = ('Master hostname: {0} not found. '
|
||||
'Retrying in {1} seconds').format(opts['master'],
|
||||
opts['retry_dns'])
|
||||
log.warn(msg)
|
||||
print msg
|
||||
time.sleep(opts['retry_dns'])
|
||||
try:
|
||||
opts['master_ip'] = salt.utils.dns_check(opts['master'], True)
|
||||
break
|
||||
except SaltClientError:
|
||||
pass
|
||||
else:
|
||||
opts['master_ip'] = '127.0.0.1'
|
||||
if check_dns:
|
||||
# Because I import salt.log bellow I need to re-import salt.utils here
|
||||
import salt.utils
|
||||
try:
|
||||
opts['master_ip'] = salt.utils.dns_check(opts['master'], True)
|
||||
except SaltClientError:
|
||||
if opts['retry_dns']:
|
||||
while True:
|
||||
import salt.log
|
||||
msg = ('Master hostname: {0} not found. Retrying in {1} '
|
||||
'seconds').format(opts['master'], opts['retry_dns'])
|
||||
if salt.log.is_console_configured():
|
||||
log.warn(msg)
|
||||
else:
|
||||
print('WARNING: {0}'.format(msg))
|
||||
time.sleep(opts['retry_dns'])
|
||||
try:
|
||||
opts['master_ip'] = salt.utils.dns_check(
|
||||
opts['master'], True
|
||||
)
|
||||
break
|
||||
except SaltClientError:
|
||||
pass
|
||||
else:
|
||||
opts['master_ip'] = '127.0.0.1'
|
||||
else:
|
||||
opts['master_ip'] = '127.0.0.1'
|
||||
|
||||
opts['master_uri'] = 'tcp://{ip}:{port}'.format(ip=opts['master_ip'],
|
||||
port=opts['master_port'])
|
||||
|
@ -96,7 +96,7 @@ class Client(object):
|
||||
yield dest
|
||||
os.umask(cumask)
|
||||
|
||||
def get_file(self, path, dest='', makedirs=False, env='base'):
|
||||
def get_file(self, path, dest='', makedirs=False, env='base', gzip=None):
|
||||
'''
|
||||
Copies a file from the local files or master depending on
|
||||
implementation
|
||||
@ -257,7 +257,7 @@ class Client(object):
|
||||
return dest
|
||||
return False
|
||||
|
||||
def get_dir(self, path, dest='', env='base'):
|
||||
def get_dir(self, path, dest='', env='base', gzip=None):
|
||||
'''
|
||||
Get a directory recursively from the salt-master
|
||||
'''
|
||||
@ -284,7 +284,7 @@ class Client(object):
|
||||
self.get_file(
|
||||
'salt://{0}'.format(fn_),
|
||||
'{0}/{1}'.format(dest, minion_relpath),
|
||||
True, env
|
||||
True, env, gzip
|
||||
)
|
||||
)
|
||||
# Replicate empty dirs from master
|
||||
@ -413,10 +413,10 @@ class LocalClient(Client):
|
||||
return fnd
|
||||
return fnd
|
||||
|
||||
def get_file(self, path, dest='', makedirs=False, env='base', gzip_compression=None):
|
||||
def get_file(self, path, dest='', makedirs=False, env='base', gzip=None):
|
||||
'''
|
||||
Copies a file from the local files directory into :param:`dest`
|
||||
gzip_compression settings are ignored for local files
|
||||
gzip compression settings are ignored for local files
|
||||
'''
|
||||
path = self._check_proto(path)
|
||||
fnd = self._find_file(path, env)
|
||||
@ -555,7 +555,7 @@ class RemoteClient(Client):
|
||||
self.auth = salt.crypt.SAuth(opts)
|
||||
self.sreq = salt.payload.SREQ(self.opts['master_uri'])
|
||||
|
||||
def get_file(self, path, dest='', makedirs=False, env='base', gzip_compression=None):
|
||||
def get_file(self, path, dest='', makedirs=False, env='base', gzip=None):
|
||||
'''
|
||||
Get a single file from the salt-master
|
||||
path must be a salt server location, aka, salt://path/to/file, if
|
||||
@ -567,9 +567,9 @@ class RemoteClient(Client):
|
||||
load = {'path': path,
|
||||
'env': env,
|
||||
'cmd': '_serve_file'}
|
||||
if gzip_compression:
|
||||
gzip_compression = int(gzip_compression)
|
||||
load['gzip_compression'] = gzip_compression
|
||||
if gzip:
|
||||
gzip = int(gzip)
|
||||
load['gzip'] = gzip
|
||||
|
||||
fn_ = None
|
||||
if dest:
|
||||
@ -608,9 +608,8 @@ class RemoteClient(Client):
|
||||
if not fn_:
|
||||
with self._cache_loc(data['dest'], env) as cache_dest:
|
||||
dest = cache_dest
|
||||
fn_ = salt.utils.fopen(dest, 'wb+')
|
||||
gzip_compression = data.get('gzip_compression', None)
|
||||
if gzip_compression:
|
||||
fn_ = open(dest, 'wb+')
|
||||
if data.get('gzip', None):
|
||||
data = salt.utils.gzip_util.uncompress(data['data'])
|
||||
else:
|
||||
data = data['data']
|
||||
|
@ -630,14 +630,14 @@ class AESFuncs(object):
|
||||
if not fnd['path']:
|
||||
return ret
|
||||
ret['dest'] = fnd['rel']
|
||||
gzip_compression = load.get('gzip_compression', None)
|
||||
gzip = load.get('gzip', None)
|
||||
|
||||
with salt.utils.fopen(fnd['path'], 'rb') as fp_:
|
||||
fp_.seek(load['loc'])
|
||||
data = fp_.read(self.opts['file_buffer_size'])
|
||||
if gzip_compression and data:
|
||||
data = salt.utils.gzip_util.compress(data, gzip_compression)
|
||||
ret['gzip_compression'] = gzip_compression
|
||||
if gzip and data:
|
||||
data = salt.utils.gzip_util.compress(data, gzip)
|
||||
ret['gzip'] = gzip
|
||||
ret['data'] = data
|
||||
return ret
|
||||
|
||||
|
@ -70,14 +70,12 @@ def version(name):
|
||||
|
||||
salt '*' pkg.version <package name>
|
||||
'''
|
||||
# check for :i386 appended to 32bit name installed on 64bit machine
|
||||
name32bit = '{0}:i386'.format(name)
|
||||
|
||||
pkgs = list_pkgs(name)
|
||||
# check for ':arch' appended to pkg name (i.e. 32 bit installed on 64 bit machine is ':i386')
|
||||
if name.find(':') >= 0:
|
||||
name = name.split(':')[0]
|
||||
if name in pkgs:
|
||||
return pkgs[name]
|
||||
if name32bit in pkgs:
|
||||
return pkgs[name32bit]
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
'''
|
||||
Manages configuration files via augeas
|
||||
|
||||
:depends: - Augeas Python adapter
|
||||
'''
|
||||
# Load Augeas libs
|
||||
try:
|
||||
|
@ -1,19 +1,14 @@
|
||||
'''
|
||||
Cassandra NoSQL Database Module
|
||||
|
||||
REQUIREMENT 1:
|
||||
:depends: - pycassa Cassandra Python adapter
|
||||
:configuration:
|
||||
The location of the 'nodetool' command, host, and thrift port needs to be
|
||||
specified via pillar::
|
||||
|
||||
The location of the 'nodetool' command, host, and thrift port
|
||||
needs to be specified via pillar.
|
||||
|
||||
cassandra.nodetool: /usr/local/bin/nodetool
|
||||
cassandra.host: localhost
|
||||
cassandra.thrift_port: 9160
|
||||
|
||||
REQUIREMENT 2:
|
||||
|
||||
The python module, 'pycassa', also needs to be installed on the
|
||||
minion.
|
||||
cassandra.nodetool: /usr/local/bin/nodetool
|
||||
cassandra.host: localhost
|
||||
cassandra.thrift_port: 9160
|
||||
'''
|
||||
# Import Python libs
|
||||
import logging
|
||||
|
@ -41,8 +41,47 @@ def recv(files, dest):
|
||||
|
||||
return ret
|
||||
|
||||
def _render_filenames(path, dest, env, template):
|
||||
if not template:
|
||||
return (path, dest)
|
||||
|
||||
def get_file(path, dest, env='base', template=None, gzip_compression=None):
|
||||
# render the path as a template using path_template_engine as the engine
|
||||
if template not in salt.utils.templates.template_registry:
|
||||
raise CommandExecutionError('Attempted to render file paths with unavailable engine '
|
||||
'{0}'.format(template))
|
||||
|
||||
kwargs = {}
|
||||
kwargs['salt'] = __salt__
|
||||
kwargs['pillar'] = __pillar__
|
||||
kwargs['grains'] = __grains__
|
||||
kwargs['opts'] = __opts__
|
||||
kwargs['env'] = env
|
||||
|
||||
def _render(contents):
|
||||
# write out path to temp file
|
||||
tmp_path_fn = salt.utils.mkstemp()
|
||||
with open(tmp_path_fn, 'w+') as fp_:
|
||||
fp_.write(contents)
|
||||
data = salt.utils.templates.template_registry[template](
|
||||
tmp_path_fn,
|
||||
to_str=True,
|
||||
**kwargs
|
||||
)
|
||||
salt.utils.safe_rm(tmp_path_fn)
|
||||
if not data['result']:
|
||||
# Failed to render the template
|
||||
raise CommandExecutionError('Failed to render file path with error: {0}'.format(
|
||||
data['data']
|
||||
))
|
||||
else:
|
||||
return data['data']
|
||||
|
||||
path = _render(path)
|
||||
dest = _render(dest)
|
||||
return (path, dest)
|
||||
|
||||
|
||||
def get_file(path, dest, env='base', makedirs=False, template=None, gzip=None):
|
||||
'''
|
||||
Used to get a single file from the salt master
|
||||
|
||||
@ -50,56 +89,13 @@ def get_file(path, dest, env='base', template=None, gzip_compression=None):
|
||||
|
||||
salt '*' cp.get_file salt://path/to/file /minion/dest
|
||||
'''
|
||||
if template is not None:
|
||||
# render the path as a template using path_template_engine as the engine
|
||||
if template not in salt.utils.templates.template_registry:
|
||||
log.error('Attempted to render file paths with unavailable engine '
|
||||
'{0}'.format(template))
|
||||
return ''
|
||||
|
||||
kwargs = {}
|
||||
kwargs['salt'] = __salt__
|
||||
kwargs['pillar'] = __pillar__
|
||||
kwargs['grains'] = __grains__
|
||||
kwargs['opts'] = __opts__
|
||||
kwargs['env'] = env
|
||||
|
||||
def _render(contents):
|
||||
# write out path to temp file
|
||||
tmp_path_fn = salt.utils.mkstemp()
|
||||
with open(tmp_path_fn, 'w+') as fp_:
|
||||
fp_.write(contents)
|
||||
data = salt.utils.templates.template_registry[template](
|
||||
tmp_path_fn,
|
||||
to_str=True,
|
||||
**kwargs
|
||||
)
|
||||
salt.utils.safe_rm(tmp_path_fn)
|
||||
if not data['result']:
|
||||
# Failed to render the template
|
||||
raise CommandExecutionError('Failed to render file path with error: {0}'.format(
|
||||
data['data']
|
||||
))
|
||||
else:
|
||||
return data['data']
|
||||
|
||||
|
||||
try:
|
||||
path = _render(path)
|
||||
except CommandExecutionError as exc:
|
||||
log.error(str(exc))
|
||||
return ''
|
||||
try:
|
||||
dest = _render(dest)
|
||||
except CommandExecutionError as exc:
|
||||
log.error(str(exc))
|
||||
return ''
|
||||
(path, dest) = _render_filenames(path, dest, env, template)
|
||||
|
||||
if not hash_file(path, env):
|
||||
return ''
|
||||
else:
|
||||
client = salt.fileclient.get_file_client(__opts__)
|
||||
return client.get_file(path, dest, False, env, gzip_compression)
|
||||
return client.get_file(path, dest, makedirs, env, gzip)
|
||||
|
||||
|
||||
def get_template(path, dest, template='jinja', env='base', **kwargs):
|
||||
@ -122,7 +118,7 @@ def get_template(path, dest, template='jinja', env='base', **kwargs):
|
||||
return client.get_template(path, dest, template, False, env, **kwargs)
|
||||
|
||||
|
||||
def get_dir(path, dest, env='base'):
|
||||
def get_dir(path, dest, env='base', template=None, gzip=None):
|
||||
'''
|
||||
Used to recursively copy a directory from the salt master
|
||||
|
||||
@ -130,8 +126,10 @@ def get_dir(path, dest, env='base'):
|
||||
|
||||
salt '*' cp.get_dir salt://path/to/dir/ /minion/dest
|
||||
'''
|
||||
(path, dest) = _render_filenames(path, dest, env, template)
|
||||
|
||||
client = salt.fileclient.get_file_client(__opts__)
|
||||
return client.get_dir(path, dest, env)
|
||||
return client.get_dir(path, dest, env, gzip)
|
||||
|
||||
|
||||
def get_url(path, dest, env='base'):
|
||||
|
@ -1,5 +1,7 @@
|
||||
'''
|
||||
Support for Portage
|
||||
|
||||
:optdepends: - portage Python adapter
|
||||
'''
|
||||
|
||||
try:
|
||||
|
@ -22,7 +22,7 @@ def _gem(command, ruby=None, runas=None):
|
||||
return False
|
||||
|
||||
|
||||
def install(gems, ruby=None, runas=None):
|
||||
def install(gems, ruby=None, runas=None, version=None, rdoc=False, ri=False):
|
||||
'''
|
||||
Installs one or several gems.
|
||||
|
||||
@ -32,8 +32,23 @@ def install(gems, ruby=None, runas=None):
|
||||
If RVM is installed, the ruby version and gemset to use.
|
||||
runas : None
|
||||
The user to run gem as.
|
||||
version : None
|
||||
Specify the version to install for the gem.
|
||||
Doesn't play nice with multiple gems at once
|
||||
rdoc : False
|
||||
Generate RDoc documentation for the gem(s).
|
||||
ri : False
|
||||
Generate RI documentation for the gem(s).
|
||||
'''
|
||||
return _gem('install {gems}'.format(gems=gems), ruby, runas=runas)
|
||||
options = ''
|
||||
if version:
|
||||
options += ' --version {0}'.format(version)
|
||||
if not rdoc:
|
||||
options += ' --no-rdoc'
|
||||
if not ri:
|
||||
options += ' --no-ri'
|
||||
|
||||
return _gem('install {gems} {options}'.format(gems=gems, options=options), ruby, runas=runas)
|
||||
|
||||
|
||||
def uninstall(gems, ruby=None, runas=None):
|
||||
|
@ -1,15 +1,16 @@
|
||||
'''
|
||||
Module for handling openstack glance calls.
|
||||
|
||||
This module is not usable until the following are specified either in a pillar
|
||||
or in the minion's config file:
|
||||
:optdepends: - glanceclient Python adapter
|
||||
:configuration: This module is not usable until the following are specified
|
||||
either in a pillar or in the minion's config file::
|
||||
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.tenant_id: f80919baedab48ec8931f200c65a50df
|
||||
keystone.insecure: False #(optional)
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.tenant_id: f80919baedab48ec8931f200c65a50df
|
||||
keystone.insecure: False #(optional)
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
'''
|
||||
has_glance = False
|
||||
try:
|
||||
|
@ -11,7 +11,7 @@ def __virtual__():
|
||||
'''
|
||||
Set the user module if the kernel is Linux
|
||||
'''
|
||||
return 'group' if __grains__['kernel'] == 'Linux' else False
|
||||
return 'group' if __grains__.get('kernel', '') == 'Linux' else False
|
||||
|
||||
|
||||
def add(name, gid=None, system=False):
|
||||
|
@ -1,15 +1,16 @@
|
||||
'''
|
||||
Module for handling openstack keystone calls.
|
||||
|
||||
This module is not usable until the following are specified either in a pillar
|
||||
or in the minion's config file:
|
||||
:optdepends: - keystoneclient Python adapter
|
||||
:configuration: This module is not usable until the following are specified
|
||||
either in a pillar or in the minion's config file::
|
||||
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.tenant_id: f80919baedab48ec8931f200c65a50df
|
||||
keystone.insecure: False #(optional)
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.tenant_id: f80919baedab48ec8931f200c65a50df
|
||||
keystone.insecure: False #(optional)
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
'''
|
||||
has_keystone = False
|
||||
try:
|
||||
|
@ -2,7 +2,7 @@
|
||||
Provide the hyper module for kvm hypervisors. This is the interface used to
|
||||
interact with kvm on behalf of the salt-virt interface
|
||||
|
||||
Required python modules: libvirt
|
||||
:depends: - libvirt Python module
|
||||
'''
|
||||
|
||||
# This is a test interface for the salt-virt system. The api in this file is
|
||||
|
@ -1,5 +1,7 @@
|
||||
'''
|
||||
Module for the management of MacOS systems that use launchd/launchctl
|
||||
|
||||
:depends: - plistlib Python module
|
||||
'''
|
||||
|
||||
import plistlib
|
||||
|
@ -1,37 +1,33 @@
|
||||
'''
|
||||
Module to provide LDAP commands via salt.
|
||||
|
||||
REQUIREMENT 1:
|
||||
:depends: - ldap Python module
|
||||
:configuration: In order to connect to LDAP, certain configuration is required
|
||||
in the minion config on the LDAP server. The minimum configuration items
|
||||
that must be set are::
|
||||
|
||||
In order to connect to LDAP, certain configuration is required
|
||||
in the minion config on the LDAP server.
|
||||
The minimum configuration items that must be set are::
|
||||
ldap.basedn: dc=acme,dc=com (example values, adjust to suit)
|
||||
|
||||
ldap.basedn: dc=acme,dc=com (example values, adjust to suit)
|
||||
If your LDAP server requires authentication then you must also set::
|
||||
|
||||
If your LDAP server requires authentication then you must also set::
|
||||
ldap.binddn: admin
|
||||
ldap.bindpw: password
|
||||
|
||||
ldap.binddn: admin
|
||||
ldap.bindpw: password
|
||||
In addition, the following optional values may be set::
|
||||
|
||||
In addition, the following optional values may be set::
|
||||
ldap.server: localhost (default=localhost, see warning below)
|
||||
ldap.port: 389 (default=389, standard port)
|
||||
ldap.tls: False (default=False, no TLS)
|
||||
ldap.scope: 2 (default=2, ldap.SCOPE_SUBTREE)
|
||||
ldap.attrs: [saltAttr] (default=None, return all attributes)
|
||||
|
||||
ldap.server: localhost (default=localhost, see warning below)
|
||||
ldap.port: 389 (default=389, standard port)
|
||||
ldap.tls: False (default=False, no TLS)
|
||||
ldap.scope: 2 (default=2, ldap.SCOPE_SUBTREE)
|
||||
ldap.attrs: [saltAttr] (default=None, return all attributes)
|
||||
.. warning::
|
||||
|
||||
WARNING:
|
||||
At the moment this module only recommends connection to LDAP services
|
||||
listening on 'localhost'. This is deliberate to avoid the potentially
|
||||
dangerous situation of multiple minions sending identical update commands to
|
||||
the same LDAP server. It's easy enough to override this behaviour,
|
||||
but badness may ensue - you have been warned.
|
||||
|
||||
REQUIREMENT 2:
|
||||
|
||||
Required python modules: ldap
|
||||
At the moment this module only recommends connection to LDAP services
|
||||
listening on 'localhost'. This is deliberate to avoid the potentially
|
||||
dangerous situation of multiple minions sending identical update commands
|
||||
to the same LDAP server. It's easy enough to override this behaviour, but
|
||||
badness may ensue - you have been warned.
|
||||
'''
|
||||
# Import Python libs
|
||||
import time
|
||||
|
@ -113,7 +113,7 @@ def persist(name, value, config='/etc/sysctl.conf'):
|
||||
# and it seems unnecessary to indent the below for
|
||||
# loop since it is a fairly large block of code.
|
||||
config_data = _fh.readlines()
|
||||
except (IOError, OSError) as exc:
|
||||
except (IOError, OSError):
|
||||
msg = 'Could not read from file: {0}'
|
||||
raise CommandExecutionError(msg.format(config))
|
||||
|
||||
|
@ -62,7 +62,8 @@ def detail(device='/dev/md0'):
|
||||
|
||||
# Lets make sure the device exists before running mdadm
|
||||
if not os.path.exists(device):
|
||||
raise CommandExecutionError("Device {0} doesn't exist!")
|
||||
msg = "Device {0} doesn't exist!"
|
||||
raise CommandExecutionError(msg.format(device))
|
||||
|
||||
cmd = 'mdadm --detail {0}'.format(device)
|
||||
for line in __salt__['cmd.run_stdout'](cmd).splitlines():
|
||||
|
@ -1,16 +1,16 @@
|
||||
'''
|
||||
Module to provide MongoDB functionality to Salt
|
||||
|
||||
This module uses PyMongo, and accepts configuration details as parameters
|
||||
as well as configuration settings:
|
||||
:configuration: This module uses PyMongo, and accepts configuration details as
|
||||
parameters as well as configuration settings::
|
||||
|
||||
mongodb.host: 'localhost'
|
||||
mongodb.port: '27017'
|
||||
mongodb.user: ''
|
||||
mongodb.password: ''
|
||||
mongodb.host: 'localhost'
|
||||
mongodb.port: '27017'
|
||||
mongodb.user: ''
|
||||
mongodb.password: ''
|
||||
|
||||
This data can also be passed into pillar. Options passed into opts will
|
||||
overwrite options passed into pillar.
|
||||
This data can also be passed into pillar. Options passed into opts will
|
||||
overwrite options passed into pillar.
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
|
@ -1,25 +1,20 @@
|
||||
'''
|
||||
Module to provide MySQL compatibility to salt.
|
||||
|
||||
REQUIREMENT 1:
|
||||
:depends: - MySQLdb Python module
|
||||
:configuration: In order to connect to MySQL, certain configuration is required
|
||||
in /etc/salt/minion on the relevant minions. Some sample configs might look
|
||||
like::
|
||||
|
||||
In order to connect to MySQL, certain configuration is required
|
||||
in /etc/salt/minion on the relevant minions. Some sample configs
|
||||
might look like::
|
||||
mysql.host: 'localhost'
|
||||
mysql.port: 3306
|
||||
mysql.user: 'root'
|
||||
mysql.pass: ''
|
||||
mysql.db: 'mysql'
|
||||
|
||||
mysql.host: 'localhost'
|
||||
mysql.port: 3306
|
||||
mysql.user: 'root'
|
||||
mysql.pass: ''
|
||||
mysql.db: 'mysql'
|
||||
You can also use a defaults file::
|
||||
|
||||
You can also use a defaults file::
|
||||
|
||||
mysql.default_file: '/etc/mysql/debian.cnf'
|
||||
|
||||
REQUIREMENT 2:
|
||||
|
||||
Required python modules: MySQLdb
|
||||
mysql.default_file: '/etc/mysql/debian.cnf'
|
||||
'''
|
||||
# Import Python libs
|
||||
import time
|
||||
|
@ -1,13 +1,15 @@
|
||||
'''
|
||||
Module for handling openstack nova calls.
|
||||
|
||||
This module is not usable until the user, password, tenant and auth url are
|
||||
specified either in a pillar or in the minion's config file. For example:
|
||||
:depends: - novaclient Python module
|
||||
:configuration: This module is not usable until the user, password, tenant and
|
||||
auth url are specified either in a pillar or in the minion's config file.
|
||||
For example::
|
||||
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
keystone.user: admin
|
||||
keystone.password: verybadpass
|
||||
keystone.tenant: admin
|
||||
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
|
||||
'''
|
||||
has_nova = False
|
||||
try:
|
||||
|
@ -1,18 +1,18 @@
|
||||
'''
|
||||
Module to provide Postgres compatibility to salt.
|
||||
|
||||
In order to connect to Postgres, certain configuration is required
|
||||
in /etc/salt/minion on the relevant minions. Some sample configs
|
||||
might look like::
|
||||
:configuration: In order to connect to Postgres, certain configuration is
|
||||
required in /etc/salt/minion on the relevant minions. Some sample configs
|
||||
might look like::
|
||||
|
||||
postgres.host: 'localhost'
|
||||
postgres.port: '5432'
|
||||
postgres.user: 'postgres'
|
||||
postgres.pass: ''
|
||||
postgres.db: 'postgres'
|
||||
postgres.host: 'localhost'
|
||||
postgres.port: '5432'
|
||||
postgres.user: 'postgres'
|
||||
postgres.pass: ''
|
||||
postgres.db: 'postgres'
|
||||
|
||||
This data can also be passed into pillar. Options passed into opts will
|
||||
overwrite options passed into pillar
|
||||
This data can also be passed into pillar. Options passed into opts will
|
||||
overwrite options passed into pillar
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
@ -2,7 +2,7 @@
|
||||
A salt interface to psutil, a system and process library.
|
||||
See http://code.google.com/p/psutil.
|
||||
|
||||
Required python modules: psutil
|
||||
:depends: - psutil Python module
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
@ -1,7 +1,7 @@
|
||||
'''
|
||||
Manage the registry on Windows
|
||||
|
||||
Required python modules: _winreg
|
||||
:depends: - winreg Python module
|
||||
'''
|
||||
|
||||
# TODO: Figure out the exceptions _winreg can raise and properly catch
|
||||
|
@ -1,6 +1,8 @@
|
||||
'''
|
||||
The Saltutil module is used to manage the state of the salt minion itself. It is
|
||||
used to manage minion modules as well as automate updates to the salt minion
|
||||
The Saltutil module is used to manage the state of the salt minion itself. It
|
||||
is used to manage minion modules as well as automate updates to the salt minion
|
||||
|
||||
:depends: - esky Python module
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
@ -3,8 +3,23 @@ Manage client ssh components
|
||||
'''
|
||||
import os
|
||||
import re
|
||||
import binascii
|
||||
import hashlib
|
||||
import binascii
|
||||
import logging
|
||||
|
||||
import salt.utils
|
||||
from salt.exceptions import (
|
||||
SaltInvocationError,
|
||||
CommandExecutionError,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def __virtual__():
|
||||
# TODO: This could work on windows with some love
|
||||
if __grains__['os'] == 'Windows':
|
||||
return False
|
||||
return 'ssh'
|
||||
|
||||
|
||||
def _refine_enc(enc):
|
||||
@ -66,25 +81,34 @@ def _replace_auth_key(
|
||||
lines = []
|
||||
uinfo = __salt__['user.info'](user)
|
||||
full = os.path.join(uinfo['home'], config)
|
||||
with open(full, 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
# Commented Line
|
||||
lines.append(line)
|
||||
continue
|
||||
comps = line.split()
|
||||
if len(comps) < 2:
|
||||
# Not a valid line
|
||||
lines.append(line)
|
||||
continue
|
||||
key_ind = 1
|
||||
if comps[0][:4:] not in ['ssh-', 'ecds']:
|
||||
key_ind = 2
|
||||
if comps[key_ind] == key:
|
||||
lines.append(auth_line)
|
||||
else:
|
||||
lines.append(line)
|
||||
with open(full, 'w+') as f: f.writelines(lines)
|
||||
try:
|
||||
# open the file for both reading AND writing
|
||||
with open(full, 'r') as _fh:
|
||||
for line in _fh:
|
||||
if line.startswith('#'):
|
||||
# Commented Line
|
||||
lines.append(line)
|
||||
continue
|
||||
comps = line.split()
|
||||
if len(comps) < 2:
|
||||
# Not a valid line
|
||||
lines.append(line)
|
||||
continue
|
||||
key_ind = 1
|
||||
if comps[0][:4:] not in ['ssh-', 'ecds']:
|
||||
key_ind = 2
|
||||
if comps[key_ind] == key:
|
||||
lines.append(auth_line)
|
||||
else:
|
||||
lines.append(line)
|
||||
_fh.close()
|
||||
# Re-open the file writable after properly closing it
|
||||
with open(full, 'w') as _fh:
|
||||
# Write out any changes
|
||||
_fh.writelines(lines)
|
||||
except (IOError, OSError) as exc:
|
||||
msg = 'Problem reading or writing to key file: {0}'
|
||||
raise CommandExecutionError(msg.format(str(exc)))
|
||||
|
||||
|
||||
def _validate_keys(key_file):
|
||||
@ -93,9 +117,10 @@ def _validate_keys(key_file):
|
||||
'''
|
||||
ret = {}
|
||||
linere = re.compile(r'^(.*?)\s?((?:ssh\-|ecds).+)$')
|
||||
|
||||
try:
|
||||
with open(key_file, 'r') as f:
|
||||
for line in f:
|
||||
with open(key_file, 'r') as _fh:
|
||||
for line in _fh:
|
||||
if line.startswith('#'):
|
||||
# Commented Line
|
||||
continue
|
||||
@ -130,8 +155,9 @@ def _validate_keys(key_file):
|
||||
'comment': comment,
|
||||
'options': options,
|
||||
'fingerprint': fingerprint}
|
||||
except IOError:
|
||||
return {}
|
||||
except (IOError, OSError) as exc:
|
||||
msg = 'Problem reading ssh key file {0}'
|
||||
raise CommandExecutionError(msg.format(key_file))
|
||||
|
||||
return ret
|
||||
|
||||
@ -162,11 +188,14 @@ def host_keys(keydir=None):
|
||||
|
||||
salt '*' ssh.host_keys
|
||||
'''
|
||||
# Set up the default keydir - needs to support sshd_config parsing in the
|
||||
# future
|
||||
# TODO: support parsing sshd_config for the key directory
|
||||
if not keydir:
|
||||
if __grains__['kernel'] == 'Linux':
|
||||
keydir = '/etc/ssh'
|
||||
else:
|
||||
# If keydir is None, os.listdir() will blow up
|
||||
msg = 'ssh.host_keys: Please specify a keydir'
|
||||
raise SaltInvocationError(msg)
|
||||
keys = {}
|
||||
for fn_ in os.listdir(keydir):
|
||||
if fn_.startswith('ssh_host_'):
|
||||
@ -176,7 +205,8 @@ def host_keys(keydir=None):
|
||||
if len(top) > 1:
|
||||
kname += '.{0}'.format(top[1])
|
||||
try:
|
||||
keys[kname] = open(os.path.join(keydir, fn_), 'r').read()
|
||||
with open(os.path.join(keydir, fn_), 'r') as _fh:
|
||||
keys[kname] = _fh.readline().strip()
|
||||
except (IOError, OSError):
|
||||
keys[kname] = ''
|
||||
return keys
|
||||
@ -225,7 +255,7 @@ def check_key(user, key, enc, comment, options, config='.ssh/authorized_keys'):
|
||||
|
||||
CLI Example::
|
||||
|
||||
salt '*' ssh.check_key <user> <key>
|
||||
salt '*' ssh.check_key <user> <key> <enc> <comment> <options>
|
||||
'''
|
||||
current = auth_keys(user, config)
|
||||
nline = _format_auth_line(key, enc, comment, options)
|
||||
@ -256,37 +286,53 @@ def rm_auth_key(user, key, config='.ssh/authorized_keys'):
|
||||
# Remove the key
|
||||
uinfo = __salt__['user.info'](user)
|
||||
full = os.path.join(uinfo['home'], config)
|
||||
|
||||
# Return something sensible if the file doesn't exist
|
||||
if not os.path.isfile(full):
|
||||
return 'User authorized keys file not present'
|
||||
return 'Authorized keys file {1} not present'.format(full)
|
||||
|
||||
lines = []
|
||||
with open(full, 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
# Commented Line
|
||||
try:
|
||||
# Read every line in the file to find the right ssh key
|
||||
# and then write out the correct one. Open the file once
|
||||
with open(full, 'r') as _fh:
|
||||
for line in _fh:
|
||||
if line.startswith('#'):
|
||||
# Commented Line
|
||||
lines.append(line)
|
||||
continue
|
||||
|
||||
# get "{options} key"
|
||||
ln = re.search(linere, line)
|
||||
if not ln:
|
||||
# not an auth ssh key, perhaps a blank line
|
||||
continue
|
||||
|
||||
comps = ln.group(2).split()
|
||||
|
||||
if len(comps) < 2:
|
||||
# Not a valid line
|
||||
lines.append(line)
|
||||
continue
|
||||
|
||||
pkey = comps[1]
|
||||
|
||||
# This is the key we are "deleting", so don't put
|
||||
# it in the list of keys to be re-added back
|
||||
if pkey == key:
|
||||
continue
|
||||
|
||||
lines.append(line)
|
||||
continue
|
||||
|
||||
# get "{options} key"
|
||||
ln = re.search(linere, line)
|
||||
if not ln:
|
||||
# not an auth ssh key, perhaps a blank line
|
||||
continue
|
||||
|
||||
comps = ln.group(2).split()
|
||||
|
||||
if len(comps) < 2:
|
||||
# Not a valid line
|
||||
lines.append(line)
|
||||
continue
|
||||
|
||||
pkey = comps[1]
|
||||
|
||||
if pkey == key:
|
||||
continue
|
||||
else:
|
||||
lines.append(line)
|
||||
with open(full, 'w+') as f: f.writelines(lines)
|
||||
# Let the context manager do the right thing here and then
|
||||
# re-open the file in write mode to save the changes out.
|
||||
with open(full, 'w') as _fh:
|
||||
_fh.writelines(lines)
|
||||
except (IOError, OSError) as exc:
|
||||
log.warn('Could not read/write key file: {0}'.format(str(exc)))
|
||||
return 'Key not removed'
|
||||
return 'Key removed'
|
||||
# TODO: Should this function return a simple boolean?
|
||||
return 'Key not present'
|
||||
|
||||
def set_auth_key_from_file(
|
||||
@ -305,7 +351,8 @@ def set_auth_key_from_file(
|
||||
# TODO: add support for pulling keys from other file sources as well
|
||||
lfile = __salt__['cp.cache_file'](source, env)
|
||||
if not os.path.isfile(lfile):
|
||||
return 'fail'
|
||||
msg = 'Failed to pull key file from salt file server'
|
||||
raise CommandExecutionError(msg)
|
||||
|
||||
newkey = {}
|
||||
rval = ''
|
||||
@ -383,12 +430,21 @@ def set_auth_key(
|
||||
os.chmod(dpath, 448)
|
||||
|
||||
if not os.path.isfile(fconfig):
|
||||
open(fconfig, 'a+').write('{0}'.format(auth_line))
|
||||
new_file = True
|
||||
else:
|
||||
new_file = False
|
||||
|
||||
try:
|
||||
with open(fconfig, 'a+') as _fh:
|
||||
_fh.write('{0}'.format(auth_line))
|
||||
except (IOError, OSError) as exc:
|
||||
msg = 'Could not write to key file: {0}'
|
||||
raise CommandExecutionError(msg.format(str(exc)))
|
||||
|
||||
if new_file:
|
||||
if os.geteuid() == 0:
|
||||
os.chown(fconfig, uinfo['uid'], uinfo['gid'])
|
||||
os.chmod(fconfig, 384)
|
||||
else:
|
||||
open(fconfig, 'a+').write('{0}'.format(auth_line))
|
||||
return 'new'
|
||||
|
||||
|
||||
@ -432,7 +488,7 @@ def get_known_host(user, hostname, config='.ssh/known_hosts'):
|
||||
|
||||
def recv_known_host(user, hostname, enc=None, port=None, hash_hostname=False):
|
||||
'''
|
||||
Retreive information about host public key from remote server
|
||||
Retrieve information about host public key from remote server
|
||||
|
||||
CLI Example::
|
||||
|
||||
@ -546,12 +602,18 @@ def set_known_host(user, hostname,
|
||||
uinfo = __salt__['user.info'](user)
|
||||
full = os.path.join(uinfo['home'], config)
|
||||
line = '{hostname} {enc} {key}\n'.format(**remote_host)
|
||||
with open(full, 'a') as fd:
|
||||
fd.write(line)
|
||||
|
||||
try:
|
||||
with open(full, 'a') as fd:
|
||||
fd.write(line)
|
||||
except (IOError, OSError) as exc:
|
||||
raise CommandExecutionError("Couldn't append to known hosts file")
|
||||
|
||||
if os.geteuid() == 0:
|
||||
os.chown(full, uinfo['uid'], uinfo['gid'])
|
||||
return {'status': 'updated', 'old': stored_host, 'new': remote_host}
|
||||
|
||||
# TODO: The lines below this are dead code, fix the above return and make these work
|
||||
status = check_known_host(user, hostname, fingerprint=fingerprint,
|
||||
config=config)
|
||||
if status == 'exists':
|
||||
|
@ -3,16 +3,11 @@ A salt module for SSL/TLS.
|
||||
Can create a Certificate Authority (CA)
|
||||
or use Self-Signed certificates.
|
||||
|
||||
REQUIREMENT 1:
|
||||
:depends: - PyOpenSSL Python module
|
||||
:configuration: Add the following values in /etc/salt/minion for the CA module
|
||||
to function properly::
|
||||
|
||||
Required python modules: PyOpenSSL
|
||||
|
||||
REQUIREMENT 2:
|
||||
|
||||
Add the following values in /etc/salt/minion for the
|
||||
CA module to function properly::
|
||||
|
||||
ca.cert_base_path: '/etc/pki'
|
||||
ca.cert_base_path: '/etc/pki'
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
@ -1,7 +1,7 @@
|
||||
'''
|
||||
Work with virtual machines managed by libvirt
|
||||
|
||||
Required python modules: libvirt
|
||||
:depends: libvirt Python module
|
||||
'''
|
||||
# Special Thanks to Michael Dehann, many of the concepts, and a few structures
|
||||
# of his in the virt func module have been used
|
||||
|
@ -1,5 +1,7 @@
|
||||
'''
|
||||
Module for gathering disk information on Windows
|
||||
|
||||
:depends: - win32api Python module
|
||||
'''
|
||||
try:
|
||||
import ctypes
|
||||
|
@ -2,7 +2,10 @@
|
||||
Manage information about files on the minion, set/read user, group
|
||||
data
|
||||
|
||||
Required python modules: win32api, win32con, win32security, ntsecuritycon
|
||||
:depends: - win32api
|
||||
- win32con
|
||||
- win32security
|
||||
- ntsecuritycon
|
||||
'''
|
||||
|
||||
import os
|
||||
|
@ -1,5 +1,10 @@
|
||||
'''
|
||||
A module to manage software on Windows
|
||||
|
||||
:depends: - pythoncom
|
||||
- win32com
|
||||
- win32con
|
||||
- win32api
|
||||
'''
|
||||
try:
|
||||
import pythoncom
|
||||
|
@ -1,7 +1,9 @@
|
||||
'''
|
||||
Support for YUM
|
||||
|
||||
Required python modules: yum, rpm, rpmUtils
|
||||
:depends: - yum Python module
|
||||
- rpm Python module
|
||||
- rpmUtils Python module
|
||||
'''
|
||||
try:
|
||||
import yum
|
||||
|
@ -3,7 +3,7 @@ zfs support.
|
||||
|
||||
Assumes FreeBSD
|
||||
|
||||
requires: mkfile
|
||||
:depends: - mkfile
|
||||
'''
|
||||
import os
|
||||
|
||||
|
@ -28,19 +28,27 @@ def get_printout(out, opts=None, **kwargs):
|
||||
'''
|
||||
if opts is None:
|
||||
opts = {}
|
||||
for outputter in STATIC:
|
||||
if outputter in opts:
|
||||
if opts[outputter]:
|
||||
if outputter == 'text_out':
|
||||
out = 'txt'
|
||||
else:
|
||||
out = outputter
|
||||
|
||||
if 'output' in opts:
|
||||
# new --out option
|
||||
out = opts['output']
|
||||
if out == 'text':
|
||||
out = 'txt'
|
||||
else:
|
||||
# XXX: This should be removed before 0.10.8 comes out
|
||||
for outputter in STATIC:
|
||||
if outputter in opts:
|
||||
if opts[outputter]:
|
||||
if outputter == 'text_out':
|
||||
out = 'txt'
|
||||
else:
|
||||
out = outputter
|
||||
if out and out.endswith('_out'):
|
||||
out = out[:-4]
|
||||
|
||||
if out is None:
|
||||
out = 'pprint'
|
||||
if out.endswith('_out'):
|
||||
out = out[:-4]
|
||||
if opts is None:
|
||||
opts = {}
|
||||
|
||||
opts.update(kwargs)
|
||||
if not 'color' in opts:
|
||||
opts['color'] = not bool(opts.get('no_color', False))
|
||||
@ -55,4 +63,3 @@ def out_format(data, out, opts=None):
|
||||
Return the formatted outputter string for the passed data
|
||||
'''
|
||||
return get_printout(out, opts)(data).rstrip()
|
||||
|
||||
|
@ -70,6 +70,7 @@ class Pillar(object):
|
||||
self.opts = self.__gen_opts(opts, grains, id_, env)
|
||||
self.client = salt.fileclient.get_file_client(self.opts)
|
||||
if opts.get('file_client', '') == 'local':
|
||||
opts['grains'] = grains
|
||||
self.functions = salt.loader.minion_mods(opts)
|
||||
else:
|
||||
self.functions = salt.loader.minion_mods(self.opts)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
from StringIO import StringIO
|
||||
|
||||
# Import Salt libs
|
||||
from salt.exceptions import SaltRenderError
|
||||
@ -6,13 +7,18 @@ import salt.utils.templates
|
||||
|
||||
|
||||
|
||||
def render(template_file, env='', sls='', context=None, **kws):
|
||||
def render(template_file, env='', sls='', argline='', context=None, **kws):
|
||||
'''
|
||||
Render the template_file, passing the functions and grains into the
|
||||
Jinja rendering system.
|
||||
|
||||
:rtype: string
|
||||
'''
|
||||
from_str = argline=='-s'
|
||||
if not from_str and argline:
|
||||
raise SaltRenderError(
|
||||
'Unknown renderer option: {opt}'.format(opt=argline)
|
||||
)
|
||||
tmp_data = salt.utils.templates.jinja(template_file, to_str=True,
|
||||
salt=__salt__,
|
||||
grains=__grains__,
|
||||
@ -24,5 +30,5 @@ def render(template_file, env='', sls='', context=None, **kws):
|
||||
if not tmp_data.get('result', False):
|
||||
raise SaltRenderError(tmp_data.get('data',
|
||||
'Unknown render error in jinja renderer'))
|
||||
return tmp_data['data']
|
||||
return StringIO(tmp_data['data'])
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
from StringIO import StringIO
|
||||
|
||||
import salt.utils.templates
|
||||
from salt.exceptions import SaltRenderError
|
||||
@ -21,4 +22,4 @@ def render(template_file, env='', sls='', context=None, **kws):
|
||||
if not tmp_data.get('result', False):
|
||||
raise SaltRenderError(tmp_data.get('data',
|
||||
'Unknown render error in mako renderer'))
|
||||
return tmp_data['data']
|
||||
return StringIO(tmp_data['data'])
|
||||
|
@ -6,14 +6,6 @@ arguments (including salt specific args, such as 'require', etc) as template
|
||||
context. The goal is to make writing reusable/configurable/ parameterized
|
||||
salt files easier and cleaner, therefore, additionally, it also:
|
||||
|
||||
- Adds support of absolute(eg, ``salt://path/to/salt/file``) and relative(eg,
|
||||
``path/to/salt/file``) template inclusion or import(ie, with ``<%include/>``
|
||||
or ``<%namespace.../>``) for Mako. Example::
|
||||
|
||||
|
||||
<%include file="templates/sls-parts.mako"/>
|
||||
<%namespace file="salt://lib/templates/utils.mako" import="helper"/>
|
||||
|
||||
- Recognizes the special state function, ``stateconf.set``, that configures a
|
||||
default list of named arguments useable within the template context of
|
||||
the salt file. Example::
|
||||
|
@ -1,17 +1,17 @@
|
||||
from StringIO import StringIO
|
||||
|
||||
# Import Salt libs
|
||||
from salt.exceptions import SaltRenderError
|
||||
import salt.utils.templates
|
||||
|
||||
|
||||
def render(template_file, env='', sls='', context=None, **kws):
|
||||
def render(template_file, env='', sls='', argline='', context=None, **kws):
|
||||
'''
|
||||
Render the data passing the functions and grains into the rendering system
|
||||
|
||||
:rtype: string
|
||||
'''
|
||||
tmp_data = salt.utils.templates.wempy(
|
||||
template_file,
|
||||
True,
|
||||
tmp_data = salt.utils.templates.wempy(template_file, to_str=True,
|
||||
salt=__salt__,
|
||||
grains=__grains__,
|
||||
opts=__opts__,
|
||||
@ -21,5 +21,5 @@ def render(template_file, env='', sls='', context=None, **kws):
|
||||
context=context)
|
||||
if not tmp_data.get('result', False):
|
||||
raise SaltRenderError(tmp_data.get('data',
|
||||
'Unknown render error in yaml_wempy renderer'))
|
||||
return tmp_data['data']
|
||||
'Unknown render error in the wempy renderer'))
|
||||
return StringIO(tmp_data['data'])
|
||||
|
@ -599,9 +599,9 @@ class State(object):
|
||||
else:
|
||||
chunk.update(arg)
|
||||
if names:
|
||||
for name in names:
|
||||
for low_name in names:
|
||||
live = copy.deepcopy(chunk)
|
||||
live['name'] = name
|
||||
live['name'] = low_name
|
||||
for fun in funcs:
|
||||
live['fun'] = fun
|
||||
chunks.append(live)
|
||||
|
@ -29,6 +29,71 @@ syslog if there is no disk space:
|
||||
> /var/log/messages:
|
||||
cmd.run:
|
||||
- unless: echo 'foo' > /tmp/.test
|
||||
|
||||
Note that when executing a command or script, the state(ie, changed or not) of
|
||||
the command is unknown to Salt's state system. Therefore, by default, the
|
||||
``cmd`` state assumes that any command execution results in a changed state.
|
||||
|
||||
This means that if a ``cmd`` state is watched by another state then the
|
||||
state that's watching will always be executed due to the `changed` state in
|
||||
the ``cmd`` state.
|
||||
|
||||
Many state functions in this module now also accept a ``stateful`` argument.
|
||||
If ``stateful`` is specified to be true then it is assumed that the command
|
||||
or script will determine its own state and communicate it back by following
|
||||
a simple protocol described below:
|
||||
|
||||
If there's nothing in the stdout of the command, then assume no changes.
|
||||
Otherwise, the stdout must be either in JSON or its `last` non-empty line
|
||||
must be a string of key=value pairs delimited by spaces(no spaces on the
|
||||
sides of ``=``).
|
||||
|
||||
If it's JSON then it must be a JSON object(ie, {}).
|
||||
If it's key=value pairs then quoting may be used to include spaces.
|
||||
(Python's shlex module is used to parse the key=value string)
|
||||
|
||||
Two special keys or attributes are recognized in the output::
|
||||
|
||||
changed: bool (ie, 'yes', 'no', 'true', 'false', case-insensitive)
|
||||
comment: str (ie, any string)
|
||||
|
||||
So, only if 'changed' is true then assume the command execution has changed
|
||||
the state, and any other key values or attributes in the output will be set
|
||||
as part of the changes.
|
||||
|
||||
If there's a comment then it will be used as the comment of the state.
|
||||
|
||||
Here's an example of how one might write a shell script for use with a
|
||||
stateful command::
|
||||
|
||||
#!/bin/bash
|
||||
#
|
||||
echo "Working hard..."
|
||||
|
||||
# writing the state line
|
||||
echo # an empty line here so the next line will be the last.
|
||||
echo "changed=yes comment=\"something's changed!\" whatever=123"
|
||||
|
||||
|
||||
And an example salt file using this module::
|
||||
|
||||
Run myscript:
|
||||
cmd.run:
|
||||
- name: /path/to/myscript
|
||||
- cwd: /
|
||||
- stateful: true
|
||||
|
||||
Run only if myscript changed something:
|
||||
cmd.wait:
|
||||
- name: echo hello
|
||||
- cwd: /
|
||||
- watch:
|
||||
- cmd: Run myscript
|
||||
|
||||
Note that if the ``cmd.wait`` state also specfies ``stateful: true``
|
||||
it can then be watched by some other states as well.
|
||||
|
||||
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
@ -524,65 +589,3 @@ def mod_watch(name, **kwargs):
|
||||
),
|
||||
'result': False}
|
||||
|
||||
|
||||
'''
|
||||
This module is similar to the built-in cmd state module except that rather
|
||||
than always resulting in a changed state, the state of a command or script
|
||||
execution is determined by the command or the script itself.
|
||||
|
||||
This allows a state to watch for changes in another state that executes
|
||||
a command or script.
|
||||
|
||||
This is done using a simple protocol similar to the one used by Ansible's
|
||||
modules. Here's how it works:
|
||||
|
||||
If there's nothing in the stdout of the command, then assume no changes.
|
||||
Otherwise, the stdout must be either in JSON or its `last` non-empty line
|
||||
must be a string of key=value pairs delimited by spaces(no spaces on the
|
||||
sides of '=').
|
||||
|
||||
If it's JSON then it must be a JSON object(ie, {}).
|
||||
If it's key=value pairs then quoting may be used to include spaces.
|
||||
(Python's shlex module is used to parse the key=value string)
|
||||
|
||||
Two special keys or attributes are recognized in the output::
|
||||
|
||||
changed: bool (ie, 'yes', 'no', 'true', 'false', case-insensitive)
|
||||
comment: str (ie, any string)
|
||||
|
||||
So, only if 'changed' is true then assume the command execution has changed
|
||||
the state, and any other key values or attributes in the output will be set
|
||||
as part of the changes.
|
||||
|
||||
If there's a comment then it will be used as the comment of the state.
|
||||
|
||||
Here's an example of how one might write a shell script for use by this state
|
||||
function::
|
||||
|
||||
#!/bin/bash
|
||||
#
|
||||
echo "Working hard..."
|
||||
|
||||
# writing the state line
|
||||
echo # an empty line here so the next line will be the last.
|
||||
echo "changed=yes comment=\"something's changed!\" whatever=123"
|
||||
|
||||
|
||||
And an example salt files using this module::
|
||||
|
||||
Run myscript:
|
||||
state.run:
|
||||
- name: /path/to/myscript
|
||||
- cwd: /
|
||||
|
||||
Run only if myscript changed something:
|
||||
cmd.wait:
|
||||
- name: echo hello
|
||||
- cwd: /
|
||||
- watch:
|
||||
- state: Run myscript
|
||||
|
||||
|
||||
Note that instead of using `cmd.wait` in the example, `state.wait` can be
|
||||
used, and in which case it can then be watched by some other states.
|
||||
'''
|
||||
|
@ -1652,7 +1652,7 @@ def rename(name, source, force=False, makedirs=False):
|
||||
|
||||
def accumulated(name, filename, text, **kwargs):
|
||||
'''
|
||||
Prepare accumulator which can me used in template in file.managed state.
|
||||
Prepare accumulator which can be used in template in file.managed state.
|
||||
accumulator dictionary becomes available in template.
|
||||
|
||||
name
|
||||
|
@ -22,7 +22,7 @@ def __virtual__():
|
||||
return 'gem' if 'gem.list' in __salt__ else False
|
||||
|
||||
|
||||
def installed(name, ruby=None, runas=None):
|
||||
def installed(name, ruby=None, runas=None, version=None, rdoc=False, ri=False):
|
||||
'''
|
||||
Make sure that a gem is installed.
|
||||
|
||||
@ -32,9 +32,22 @@ def installed(name, ruby=None, runas=None):
|
||||
For RVM installations: the ruby version and gemset to target.
|
||||
runas : None
|
||||
The user to run gem as.
|
||||
version : None
|
||||
Specify the version to install for the gem.
|
||||
Doesn't play nice with multiple gems at once
|
||||
rdoc : False
|
||||
Generate RDoc documentation for the gem(s).
|
||||
ri : False
|
||||
Generate RI documentation for the gem(s).
|
||||
'''
|
||||
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
|
||||
if name in __salt__['gem.list'](name, ruby, runas=runas):
|
||||
|
||||
gems = __salt__['gem.list'](name, ruby, runas=runas)
|
||||
if name in gems and version and version in gems[name]:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Gem is already installed.'
|
||||
return ret
|
||||
elif name in gems:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Gem is already installed.'
|
||||
return ret
|
||||
@ -42,7 +55,12 @@ def installed(name, ruby=None, runas=None):
|
||||
if __opts__['test']:
|
||||
ret['comment'] = 'The gem {0} would have been installed'.format(name)
|
||||
return ret
|
||||
if __salt__['gem.install'](name, ruby, runas=runas):
|
||||
if __salt__['gem.install'](name,
|
||||
ruby=ruby,
|
||||
runas=runas,
|
||||
version=version,
|
||||
rdoc=rdoc,
|
||||
ri=ri):
|
||||
ret['result'] = True
|
||||
ret['changes'][name] = 'Installed'
|
||||
ret['comment'] = 'Gem was successfully installed'
|
||||
|
@ -23,6 +23,7 @@ def installed(name,
|
||||
log=None,
|
||||
proxy=None,
|
||||
timeout=None,
|
||||
repo=None,
|
||||
editable=None,
|
||||
find_links=None,
|
||||
index_url=None,
|
||||
@ -69,7 +70,7 @@ def installed(name,
|
||||
ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err)
|
||||
return ret
|
||||
|
||||
if ignore_installed == False and name in pip_list:
|
||||
if ignore_installed == False and name.lower() in (p.lower() for p in pip_list):
|
||||
if force_reinstall == False and upgrade == False:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Package already installed'
|
||||
@ -81,6 +82,9 @@ def installed(name,
|
||||
name)
|
||||
return ret
|
||||
|
||||
if repo:
|
||||
name = repo
|
||||
|
||||
pip_install_call = __salt__['pip.install'](
|
||||
pkgs=name,
|
||||
requirements=requirements,
|
||||
|
@ -110,7 +110,7 @@ def absent(name,
|
||||
ret['comment'] = 'User {0} is not present'.format(name)
|
||||
else:
|
||||
if user_exists:
|
||||
result = __salt__['rabbitmq.delete_user']
|
||||
result = __salt__['rabbitmq.delete_user'](name, runas=runas)
|
||||
if 'Error' in result:
|
||||
ret['result'] = False
|
||||
ret['comment'] = result['Error']
|
||||
|
@ -100,10 +100,12 @@ def latest(name,
|
||||
ret['comment'] = out
|
||||
return ret
|
||||
|
||||
def dirty(target,
|
||||
def dirty(name,
|
||||
target,
|
||||
user=None,
|
||||
ignore_unversioned=False):
|
||||
'''
|
||||
Determine if the working directory has been changed.
|
||||
'''
|
||||
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
|
||||
return _fail(ret, 'This function is not implemented yet.')
|
||||
|
@ -747,8 +747,8 @@ def mkstemp(*args, **kwargs):
|
||||
'''
|
||||
close_fd = kwargs.pop('close_fd', True)
|
||||
fd_, fpath = tempfile.mkstemp(*args, **kwargs)
|
||||
if close_fd is True:
|
||||
os.close(fd_)
|
||||
del(fd_)
|
||||
return fpath
|
||||
return (fd_, fpath)
|
||||
if close_fd is False:
|
||||
return (fd_, fpath)
|
||||
os.close(fd_)
|
||||
del(fd_)
|
||||
return fpath
|
||||
|
@ -6,8 +6,7 @@ from os import path
|
||||
import logging
|
||||
|
||||
# Import third-party libs
|
||||
from jinja2 import (BaseLoader, Environment, StrictUndefined,
|
||||
FileSystemLoader)
|
||||
from jinja2 import BaseLoader
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
|
||||
# Import Salt libs
|
||||
|
@ -16,6 +16,11 @@ class SaltMakoTemplateLookup(TemplateCollection):
|
||||
If URL is an absolute path then it's treated as if it has been prefixed
|
||||
with salt://.
|
||||
|
||||
Examples::
|
||||
|
||||
<%include file="templates/sls-parts.mako"/>
|
||||
<%namespace file="salt://lib/templates/utils.mako" import="helper"/>
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, opts, env='base'):
|
||||
|
@ -12,7 +12,7 @@ import sys
|
||||
import logging
|
||||
import optparse
|
||||
from functools import partial
|
||||
from salt import config, log, version
|
||||
from salt import config, loader, log, version
|
||||
|
||||
|
||||
def _sorted(mixins_or_funcs):
|
||||
@ -297,12 +297,24 @@ class LogLevelMixIn(object):
|
||||
|
||||
def setup_logfile_logger(self):
|
||||
lfkey = 'key_logfile' if 'key' in self.get_prog_name() else 'log_file'
|
||||
if self.config.get('log_level_logfile', None) is None:
|
||||
# Remove it from config so it inherits from log_level
|
||||
self.config.pop('log_level_logfile', None)
|
||||
loglevel = self.config.get(
|
||||
'log_level_logfile', self.config['log_level']
|
||||
)
|
||||
|
||||
if self.config.get('log_fmt_logfile', None) is None:
|
||||
# Remove it from config so it inherits from log_fmt_console
|
||||
self.config.pop('log_fmt_logfile', None)
|
||||
logfmt = self.config.get(
|
||||
'log_fmt_logfile', self.config['log_fmt_console']
|
||||
)
|
||||
|
||||
if self.config.get('log_datefmt', None) is None:
|
||||
# Remove it from config so it get's the default value bellow
|
||||
self.config.pop('log_datefmt', None)
|
||||
|
||||
datefmt = self.config.get('log_datefmt', '%Y-%m-%d %H:%M:%S')
|
||||
log.setup_logfile_logger(
|
||||
self.config[lfkey],
|
||||
@ -536,30 +548,55 @@ class OutputOptionsMixIn(object):
|
||||
'--raw-out',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=('Print the output from the salt-key command in raw python '
|
||||
help=('Print the output from the \'{0}\' command in raw python '
|
||||
'form, this is suitable for re-reading the output into an '
|
||||
'executing python script with eval.')
|
||||
'executing python script with eval.'.format(
|
||||
self.get_prog_name()
|
||||
))
|
||||
)
|
||||
group.add_option(
|
||||
'--yaml-out',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Print the output from the salt-key command in yaml.'
|
||||
help='Print the output from the \'{0}\' command in yaml.'.format(
|
||||
self.get_prog_name()
|
||||
)
|
||||
)
|
||||
group.add_option(
|
||||
'--json-out',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Print the output from the salt-key command in json.'
|
||||
help='Print the output from the \'{0}\' command in json.'.format(
|
||||
self.get_prog_name()
|
||||
)
|
||||
)
|
||||
if self._include_text_out_:
|
||||
group.add_option(
|
||||
'--text-out',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help=('Print the output from the salt command in the same '
|
||||
'form the shell would.')
|
||||
help=('Print the output from the \'{0}\' command in the same '
|
||||
'form the shell would.'.format(self.get_prog_name()))
|
||||
)
|
||||
|
||||
outputters = loader.outputters(
|
||||
config.minion_config(
|
||||
'/etc/salt/minion', check_dns=False
|
||||
)
|
||||
)
|
||||
|
||||
group.add_option(
|
||||
'--out', '--output',
|
||||
dest='output',
|
||||
choices=outputters.keys(),
|
||||
help=(
|
||||
'Print the output from the \'{0}\' command using the '
|
||||
'specified outputter. One of {1}.'.format(
|
||||
self.get_prog_name(),
|
||||
', '.join([repr(k) for k in outputters])
|
||||
)
|
||||
)
|
||||
)
|
||||
group.add_option(
|
||||
'--no-color',
|
||||
default=False,
|
||||
@ -567,19 +604,50 @@ class OutputOptionsMixIn(object):
|
||||
help='Disable all colored output'
|
||||
)
|
||||
|
||||
for option in group.option_list:
|
||||
for option in self.output_options_group.option_list:
|
||||
def process(opt):
|
||||
if getattr(self.options, opt.dest):
|
||||
self.selected_output_option = opt.dest
|
||||
default = self.defaults.get(opt.dest)
|
||||
if getattr(self.options, opt.dest, default) is False:
|
||||
return
|
||||
|
||||
# XXX: CLEAN THIS CODE WHEN 0.10.8 is about to come out
|
||||
if version.__version_info__ >= (0, 10, 7):
|
||||
self.error(
|
||||
'The option {0} is deprecated. You must now use '
|
||||
'\'--out {1}\' instead.'.format(
|
||||
opt.get_opt_string(),
|
||||
opt.dest.split('_', 1)[0]
|
||||
)
|
||||
)
|
||||
|
||||
if opt.dest != 'out':
|
||||
msg = (
|
||||
'The option {0} is deprecated. Please consider using '
|
||||
'\'--out {1}\' instead.'.format(
|
||||
opt.get_opt_string(),
|
||||
opt.dest.split('_', 1)[0]
|
||||
)
|
||||
)
|
||||
if log.is_console_configured():
|
||||
logging.getLogger(__name__).warning(msg)
|
||||
else:
|
||||
sys.stdout.write('WARNING: {0}\n'.format(msg))
|
||||
|
||||
self.selected_output_option = opt.dest
|
||||
|
||||
funcname = 'process_{0}'.format(option.dest)
|
||||
if not hasattr(self, funcname):
|
||||
setattr(self, funcname, partial(process, option))
|
||||
|
||||
def process_output(self):
|
||||
self.selected_output_option = self.options.output
|
||||
|
||||
def _mixin_after_parsed(self):
|
||||
group_options_selected = filter(
|
||||
lambda option: getattr(self.options, option.dest) and
|
||||
option.dest.endswith('_out'),
|
||||
lambda option: (
|
||||
getattr(self.options, option.dest) and
|
||||
(option.dest.endswith('_out') or option.dest=='output')
|
||||
),
|
||||
self.output_options_group.option_list
|
||||
)
|
||||
if len(group_options_selected) > 1:
|
||||
|
@ -25,7 +25,7 @@ def set_pidfile(pidfile, user):
|
||||
if os.environ['os'].startswith('Windows'):
|
||||
return True
|
||||
import pwd # after confirming not running Windows
|
||||
import grp
|
||||
#import grp
|
||||
try:
|
||||
pwnam = pwd.getpwnam(user)
|
||||
uid = pwnam[2]
|
||||
|
@ -45,6 +45,7 @@ def wrap_tmpl_func(render_str):
|
||||
tmplstr = tmplsrc.read()
|
||||
else: # assume tmplsrc is file-like.
|
||||
tmplstr = tmplsrc.read()
|
||||
tmplsrc.close()
|
||||
try:
|
||||
output = render_str(tmplstr, context)
|
||||
except SaltTemplateRenderError, exc:
|
||||
|
@ -1,24 +1,39 @@
|
||||
'''
|
||||
Set up the version of Salt
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
import sys
|
||||
|
||||
__version_info__ = (0, 10, 4)
|
||||
__version_info__ = (0, 10, 5)
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
|
||||
|
||||
# If we can get a version from Git use that instead, otherwise carry on
|
||||
try:
|
||||
import os
|
||||
import subprocess
|
||||
from salt.utils import which
|
||||
|
||||
git = which('git')
|
||||
if git:
|
||||
p = subprocess.Popen([git, 'describe'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
||||
p = subprocess.Popen(
|
||||
[git, 'describe'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=True,
|
||||
cwd=os.path.abspath(os.path.dirname(__file__))
|
||||
)
|
||||
out, err = p.communicate()
|
||||
if out:
|
||||
__version__ = '{0}'.format(out.strip().lstrip('v'))
|
||||
__version_info__ = tuple(__version__.split('-', 1)[0].split('.'))
|
||||
__version_info__ = tuple(
|
||||
[int(i) for i in __version__.split('-', 1)[0].split('.')]
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def versions_report():
|
||||
libs = (
|
||||
("Jinja2", "jinja2", "__version__"),
|
||||
|
@ -19,3 +19,5 @@ integration.test: True
|
||||
# Grains addons
|
||||
grains:
|
||||
test_grain: cheese
|
||||
script: grail
|
||||
alot: many
|
||||
|
@ -57,7 +57,7 @@ class CPModuleTest(integration.ModuleCase):
|
||||
'salt://file.big',
|
||||
tgt,
|
||||
],
|
||||
gzip_compression=5
|
||||
gzip=5
|
||||
)
|
||||
with open(tgt, 'r') as scene:
|
||||
data = scene.read()
|
||||
@ -65,6 +65,24 @@ class CPModuleTest(integration.ModuleCase):
|
||||
self.assertNotIn('bacon', data)
|
||||
self.assertEqual(hash, hashlib.md5(data).hexdigest())
|
||||
|
||||
def test_get_file_makedirs(self):
|
||||
'''
|
||||
cp.get_file
|
||||
'''
|
||||
tgt = os.path.join(integration.TMP, 'make/dirs/scene33')
|
||||
self.run_function(
|
||||
'cp.get_file',
|
||||
[
|
||||
'salt://grail/scene33',
|
||||
tgt,
|
||||
],
|
||||
makedirs=True
|
||||
)
|
||||
with open(tgt, 'r') as scene:
|
||||
data = scene.read()
|
||||
self.assertIn('KNIGHT: They\'re nervous, sire.', data)
|
||||
self.assertNotIn('bacon', data)
|
||||
|
||||
def test_get_template(self):
|
||||
'''
|
||||
cp.get_template
|
||||
@ -98,6 +116,23 @@ class CPModuleTest(integration.ModuleCase):
|
||||
self.assertIn('empty', os.listdir(os.path.join(tgt, 'grail')))
|
||||
self.assertIn('scene', os.listdir(os.path.join(tgt, 'grail', '36')))
|
||||
|
||||
def test_get_dir_templated_paths(self):
|
||||
'''
|
||||
cp.get_dir
|
||||
'''
|
||||
tgt = os.path.join(integration.TMP, 'many')
|
||||
self.run_function(
|
||||
'cp.get_dir',
|
||||
[
|
||||
'salt://{{grains.script}}',
|
||||
tgt.replace('many', '{{grains.alot}}')
|
||||
]
|
||||
)
|
||||
self.assertIn('grail', os.listdir(tgt))
|
||||
self.assertIn('36', os.listdir(os.path.join(tgt, 'grail')))
|
||||
self.assertIn('empty', os.listdir(os.path.join(tgt, 'grail')))
|
||||
self.assertIn('scene', os.listdir(os.path.join(tgt, 'grail', '36')))
|
||||
|
||||
def test_get_url(self):
|
||||
'''
|
||||
cp.get_url
|
||||
|
@ -10,6 +10,7 @@
|
||||
import sys
|
||||
|
||||
# Import salt libs
|
||||
from salt import version
|
||||
from saltunittest import TestLoader, TextTestRunner, skipIf
|
||||
import integration
|
||||
from integration import TestDaemon
|
||||
@ -30,9 +31,19 @@ class CallTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
||||
|
||||
def test_text_output(self):
|
||||
out = self.run_call('--text-out test.fib 3')
|
||||
self.assertEqual(
|
||||
'local: ([0, 1, 1, 2]', ''.join(out).rsplit(",", 1)[0]
|
||||
)
|
||||
if version.__version_info__ < (0, 10, 8):
|
||||
expect = [
|
||||
"WARNING: The option --text-out is deprecated. Please "
|
||||
"consider using '--out text' instead."
|
||||
]
|
||||
else:
|
||||
expect = []
|
||||
|
||||
expect += [
|
||||
'local: ([0, 1, 1, 2]'
|
||||
]
|
||||
|
||||
self.assertEqual(''.join(expect), ''.join(out).rsplit(",", 1)[0])
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'This test does not apply on Win')
|
||||
def test_user_delete_kw_output(self):
|
||||
|
@ -5,6 +5,7 @@ import shutil
|
||||
import tempfile
|
||||
|
||||
# Import salt libs
|
||||
from salt import version
|
||||
from saltunittest import TestLoader, TextTestRunner
|
||||
import integration
|
||||
from integration import TestDaemon
|
||||
@ -37,8 +38,15 @@ class KeyTest(integration.ShellCase,
|
||||
test salt-key -L --json-out
|
||||
'''
|
||||
data = self.run_key('-L --json-out')
|
||||
if version.__version_info__ < (0, 10, 8):
|
||||
expect = [
|
||||
"WARNING: The option --json-out is deprecated. Please "
|
||||
"consider using '--out json' instead."
|
||||
]
|
||||
else:
|
||||
expect = []
|
||||
|
||||
expect = [
|
||||
expect += [
|
||||
'{',
|
||||
' "minions_rejected": [], ',
|
||||
' "minions_pre": [], ',
|
||||
@ -55,7 +63,15 @@ class KeyTest(integration.ShellCase,
|
||||
test salt-key -L --yaml-out
|
||||
'''
|
||||
data = self.run_key('-L --yaml-out')
|
||||
expect = [
|
||||
if version.__version_info__ < (0, 10, 8):
|
||||
expect = [
|
||||
"WARNING: The option --yaml-out is deprecated. Please "
|
||||
"consider using '--out yaml' instead."
|
||||
]
|
||||
else:
|
||||
expect = []
|
||||
|
||||
expect += [
|
||||
'minions:',
|
||||
'- minion',
|
||||
'- sub_minion',
|
||||
@ -69,7 +85,15 @@ class KeyTest(integration.ShellCase,
|
||||
test salt-key -L --raw-out
|
||||
'''
|
||||
data = self.run_key('-L --raw-out')
|
||||
expect = [
|
||||
if version.__version_info__ < (0, 10, 8):
|
||||
expect = [
|
||||
"WARNING: The option --raw-out is deprecated. Please "
|
||||
"consider using '--out raw' instead."
|
||||
]
|
||||
else:
|
||||
expect = []
|
||||
|
||||
expect += [
|
||||
"{'minions_rejected': [], 'minions_pre': [], "
|
||||
"'minions': ['minion', 'sub_minion']}"
|
||||
]
|
||||
|
@ -19,7 +19,7 @@ gem.__opts__ = {'test': False}
|
||||
class TestGemState(TestCase):
|
||||
|
||||
def test_installed(self):
|
||||
gems = ['foo', 'bar']
|
||||
gems = {'foo' : ['1.0'], 'bar' : ['2.0']}
|
||||
gem_list = MagicMock(return_value=gems)
|
||||
gem_install_succeeds = MagicMock(return_value=True)
|
||||
gem_install_fails = MagicMock(return_value=False)
|
||||
@ -32,14 +32,14 @@ class TestGemState(TestCase):
|
||||
ret = gem.installed('quux')
|
||||
self.assertEqual(True, ret['result'])
|
||||
gem_install_succeeds.assert_called_once_with(
|
||||
'quux', None, runas=None)
|
||||
'quux', ruby=None, runas=None, version=None, rdoc=False, ri=False)
|
||||
|
||||
with patch.dict(gem.__salt__,
|
||||
{'gem.install': gem_install_fails}):
|
||||
ret = gem.installed('quux')
|
||||
self.assertEqual(False, ret['result'])
|
||||
gem_install_fails.assert_called_once_with(
|
||||
'quux', None, runas=None)
|
||||
'quux', ruby=None, runas=None, version=None, rdoc=False, ri=False)
|
||||
|
||||
def test_removed(self):
|
||||
gems = ['foo', 'bar']
|
||||
|
Loading…
Reference in New Issue
Block a user