Merge remote-tracking branch 'upstream/2015.5' into merge-forward-2015.8

Conflicts:
    doc/topics/cloud/vmware.rst
    salt/cloud/__init__.py
    salt/cloud/clouds/vmware.py
    salt/loader.py
    salt/minion.py
    salt/modules/archive.py
    salt/modules/file.py
    salt/states/pkg.py
    salt/utils/http.py
This commit is contained in:
Colton Myers 2015-08-10 13:21:57 -06:00
commit 0fc16040d6
29 changed files with 799 additions and 610 deletions

View File

@ -224,6 +224,11 @@ rst_prolog = """\
.. _`salt-users`: https://groups.google.com/forum/#!forum/salt-users
.. _`salt-announce`: https://groups.google.com/forum/#!forum/salt-announce
.. _`salt-packagers`: https://groups.google.com/forum/#!forum/salt-packagers
.. |windownload| raw:: html
<a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-x86-Setup.exe"><strong>Salt-Minion-{release}-2-x86-Setup.exe</strong></a>
| <a href="https://repo.saltstack.com/windows/Salt-Minion-{release}-2-x86-Setup.exe.md5"><strong>md5</strong></a>
""".format(release=release)
# A shortcut for linking to tickets on the GitHub issue tracker

View File

@ -18,139 +18,16 @@ Windows Installer
Salt Minion Windows installers can be found here. The output of `md5sum <salt
minion exe>` should match the contents of the corresponding md5 file.
.. admonition:: Download here
**Latest stable build from the selected branch**:
|windownload|
* 2015.5.2
* `Salt-Minion-2015.5.2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.2-AMD64-Setup.exe.md5>`__
`Earlier builds from supported branches <https://repo.saltstack.com/windows/>`__
* 2015.5.1-3
* `Salt-Minion-2015.5.1-3-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.1-3-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.1-3-AMD64-Setup.exe.md5>`__
* 2015.5.0-2
* `Salt-Minion-2015.5.0-2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-x86-Setup.exe.md5>`__
* `Salt-Minion-2015.5.0-2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2015.5.0-2-AMD64-Setup.exe.md5>`__
* 2014.7.5-2
* `Salt-Minion-2014.7.5-2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.5-2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.5-2-x86-Setup.exe.md5>`__
* `Salt-Minion-2014.7.5-2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.5-2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.5-2-AMD64-Setup.exe.md5>`__
* 2014.7.4
* `Salt-Minion-2014.7.4-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.4-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.4-x86-Setup.exe.md5>`__
* `Salt-Minion-2014.7.4-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.4-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.4-AMD64-Setup.exe.md5>`__
* 2014.7.2
* `Salt-Minion-2014.7.2-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.2-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.2-x86-Setup.exe.md5>`__
* `Salt-Minion-2014.7.2-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.2-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.2-AMD64-Setup.exe.md5>`__
* 2014.7.1
* `Salt-Minion-2014.7.1-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.1-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.1-x86-Setup.exe.md5>`__
* `Salt-Minion-2014.7.1-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.1-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.7.1-AMD64-Setup.exe.md5>`__
* 2014.7.0
* Salt-Minion-2014.7.0-1-win32-Setup.exe | md5
* Salt-Minion-2014.7.0-AMD64-Setup.exe | md5
.. note::
The 2014.7.0 installers have been removed because of a regression. Please use the 2014.7.1 release instead.
* 2014.1.13
* `Salt-Minion-2014.1.13-x86-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-x86-Setup.exe.md5>`__
* `Salt-Minion-2014.1.13-AMD64-Setup.exe <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-AMD64-Setup.exe>`__ | `md5 <http://docs.saltstack.com/downloads/Salt-Minion-2014.1.13-AMD64-Setup.exe.md5>`__
* 2014.1.11
* `Salt-Minion-2014.1.11-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.11-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.11-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.11-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.11-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.11-AMD64-Setup.exe.md5>`__
* 2014.1.10
* `Salt-Minion-2014.1.10-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.10-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.10-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.10-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.10-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.10-AMD64-Setup.exe.md5>`__
* 2014.1.7
* `Salt-Minion-2014.1.7-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.7-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.7-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.7-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.7-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.7-AMD64-Setup.exe.md5>`__
* 2014.1.5
* `Salt-Minion-2014.1.5-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.5-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.5-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.5-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.5-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.5-AMD64-Setup.exe.md5>`__
* 2014.1.4
* `Salt-Minion-2014.1.4-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.4-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.4-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.4-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.4-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.4-AMD64-Setup.exe.md5>`__
* 2014.1.3-1 (packaging bugfix)
* `Salt-Minion-2014.1.3-1-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-1-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-1-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.3-1-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-1-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-1-AMD64-Setup.exe.md5>`__
* 2014.1.3
* `Salt-Minion-2014.1.3-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.3-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.3-AMD64-Setup.exe.md5>`__
* 2014.1.1
* `Salt-Minion-2014.1.1-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.1-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.1-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.1-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.1-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.1-AMD64-Setup.exe.md5>`__
* 2014.1.0
* `Salt-Minion-2014.1.0-win32-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.0-win32-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.0-win32-Setup.exe.md5>`__
* `Salt-Minion-2014.1.0-AMD64-Setup.exe <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.0-AMD64-Setup.exe>`__ | `md5 <https://docs.saltstack.com/downloads/Salt-Minion-2014.1.0-AMD64-Setup.exe.md5>`__
* 0.17.5-2 (bugfix release)
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.5-2-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.5-2-AMD64-Setup.exe
* 0.17.5
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.5-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.5-AMD64-Setup.exe
* 0.17.4
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.4-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.4-AMD64-Setup.exe
* 0.17.2
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.2-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.2-AMD64-Setup.exe
* 0.17.1.1 - Windows Installer bugfix release
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.1.1-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.1.1-AMD64-Setup.exe
* 0.17.1
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.1-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.1-AMD64-Setup.exe
* 0.17.0
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.0-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.17.0-AMD64-Setup.exe
* 0.16.3
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.3-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.3-AMD64-Setup.exe
* 0.16.2
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.2-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.2-AMD64-Setup.exe
* 0.16.0
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.0-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.16.0-AMD64-Setup.exe
* 0.15.3
* https://docs.saltstack.com/downloads/Salt-Minion-0.15.3-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.15.3-AMD64-Setup.exe
* 0.14.1
* https://docs.saltstack.com/downloads/Salt-Minion-0.14.1-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.14.1-AMD64-Setup.exe
* 0.14.0
* https://docs.saltstack.com/downloads/Salt-Minion-0.14.0-win32-Setup.exe
* https://docs.saltstack.com/downloads/Salt-Minion-0.14.0-AMD64-Setup.exe
`Archived builds from unsupported branches <https://repo.saltstack.com/archive/>`__
.. note::
The executables above will install dependencies that the Salt minion
The installation executable installs dependencies that the Salt minion
requires.
The 64bit installer has been tested on Windows 7 64bit and Windows Server

View File

@ -39,7 +39,7 @@ any way with the minion that started it.
To create support for a proxied device one needs to create four things:
1. The `proxytype connection class`_ (located in salt/proxy).
1. The `proxy_connection_module`_ (located in salt/proxy).
2. The `grains support code`_ (located in salt/grains).
3. :ref:`Salt modules <all-salt.modules>` specific to the controlled
device.
@ -156,118 +156,289 @@ to control a particular device. That proxy-minion process will initiate
a connection back to the master to enable control.
.. _proxytype connection class:
.. _proxy_connection_module:
Proxytypes
##########
Proxymodules
############
A proxytype is a Python class called 'Proxyconn' that encapsulates all the code
necessary to interface with a device. Proxytypes are located inside the
salt.proxy module. At a minimum a proxytype object must implement the
following methods:
A proxy module encapsulates all the code necessary to interface with a device.
Proxymodules are located inside the salt.proxy module. At a minimum
a proxymodule object must implement the following functions:
``proxytype(self)``: Returns a string with the name of the proxy type.
``__virtual__()``: This function performs the same duty that it does for other
types of Salt modules. Logic goes here to determine if the module can be
loaded, checking for the presence of Python modules on which the proxy deepends.
Returning ``False`` will prevent the module from loading.
``proxyconn(self, **kwargs)``: Provides the primary way to connect and communicate
with the device. Some proxyconns instantiate a particular object that opens a
network connection to a device and leaves the connection open for communication.
Others simply abstract a serial connection or even implement endpoints to communicate
via REST over HTTP.
``init(opts)``: Perform any initialization that the device needs. This is
a good place to bring up a persistent connection to a device, or authenticate
to create a persistent authorization token.
``id(self, opts)``: Returns a unique, unchanging id for the controlled device. This is
``id(opts)``: Returns a unique, unchanging id for the controlled device. This is
the "name" of the device, and is used by the salt-master for targeting and key
authentication.
Optionally, the class may define a ``shutdown(self, opts)`` method if the
controlled device should be informed when the minion goes away cleanly.
``shutdown()``: Code to cleanly shut down or close a connection to
a controlled device goes here. This function must exist, but can contain only
the keyword ``pass`` if there is no shutdown logic required.
It is highly recommended that the ``test.ping`` execution module also be defined
for a proxytype. The code for ``ping`` should contact the controlled device and make
sure it is really available.
``ping()``: While not required, it is highly recommended that this function also
be defined in the proxymodule. The code for ``ping`` should contact the
controlled device and make sure it is really available.
Here is an example proxytype used to interface to Juniper Networks devices that run
the Junos operating system. Note the additional library requirements--most of the
"hard part" of talking to these devices is handled by the jnpr.junos, jnpr.junos.utils,
and jnpr.junos.cfg modules.
Here is an example proxymodule used to interface to a *very* simple REST
server. Code for the server is in the `salt-contrib GitHub repository <https://github.com/saltstack/salt-contrib/proxyminion_rest_example>`_
This proxymodule enables "service" enumration, starting, stopping, restarting,
and status; "package" installation, and a ping.
.. code-block:: python
# -*- coding: utf-8 -*-
'''
This is a simple proxy-minion designed to connect to and communicate with
the bottle-based web service contained in
https://github.com/saltstack/salt-contrib/proxyminion_rest_example
'''
from __future__ import absolute_import
# Import python libs
import logging
import os
import salt.utils.http
import jnpr.junos
import jnpr.junos.utils
import jnpr.junos.cfg
HAS_JUNOS = True
HAS_REST_EXAMPLE = True
class Proxyconn(object):
# This must be present or the Salt loader won't load this module
__proxyenabled__ = ['rest_sample']
def __init__(self, details):
self.conn = jnpr.junos.Device(user=details['username'], host=details['host'], password=details['passwd'])
self.conn.open()
self.conn.bind(cu=jnpr.junos.cfg.Resource)
# Variables are scoped to this module so we can have persistent data
# across calls to fns in here.
GRAINS_CACHE = {}
DETAILS = {}
# Want logging!
log = logging.getLogger(__file__)
def proxytype(self):
return 'junos'
# This does nothing, it's here just as an example and to provide a log
# entry when the module is loaded.
def __virtual__():
'''
Only return if all the modules are available
'''
log.debug('rest_sample proxy __virtual__() called...')
return True
# Every proxy module needs an 'init', though you can
# just put a 'pass' here if it doesn't need to do anything.
def init(opts):
log.debug('rest_sample proxy init() called...')
# Save the REST URL
DETAILS['url'] = opts['proxy']['url']
# Make sure the REST URL ends with a '/'
if not DETAILS['url'].endswith('/'):
DETAILS['url'] += '/'
def id(self, opts):
return self.conn.facts['hostname']
def id(opts):
'''
Return a unique ID for this proxy minion. This ID MUST NOT CHANGE.
If it changes while the proxy is running the salt-master will get
really confused and may stop talking to this minion
'''
r = salt.utils.http.query(opts['proxy']['url']+'id', decode_type='json', decode=True)
return r['dict']['id'].encode('ascii', 'ignore')
def ping(self):
return self.conn.connected
def grains():
'''
Get the grains from the proxied device
'''
if not GRAINS_CACHE:
r = salt.utils.http.query(DETAILS['url']+'info', decode_type='json', decode=True)
GRAINS_CACHE = r['dict']
return GRAINS_CACHE
def shutdown(self, opts):
def grains_refresh():
'''
Refresh the grains from the proxied device
'''
GRAINS_CACHE = {}
return grains()
print('Proxy module {} shutting down!!'.format(opts['id']))
try:
self.conn.close()
except Exception:
pass
def service_start(name):
'''
Start a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/start/'+name, decode_type='json', decode=True)
return r['dict']
def service_stop(name):
'''
Stop a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/stop/'+name, decode_type='json', decode=True)
return r['dict']
def service_restart(name):
'''
Restart a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/restart/'+name, decode_type='json', decode=True)
return r['dict']
def service_list():
'''
List "services" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/list', decode_type='json', decode=True)
return r['dict']
def service_status(name):
'''
Check if a service is running on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/status/'+name, decode_type='json', decode=True)
return r['dict']
def package_list():
'''
List "packages" installed on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/list', decode_type='json', decode=True)
return r['dict']
def package_install(name, **kwargs):
'''
Install a "package" on the REST server
'''
cmd = DETAILS['url']+'package/install/'+name
if 'version' in kwargs:
cmd += '/'+kwargs['version']
else:
cmd += '/1.0'
r = salt.utils.http.query(cmd, decode_type='json', decode=True)
def package_remove(name):
'''
Remove a "package" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/remove/'+name, decode_type='json', decode=True)
return r['dict']
def package_status(name):
'''
Check the installation status of a package on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/status/'+name, decode_type='json', decode=True)
return r['dict']
def ping():
'''
Is the REST server up?
'''
r = salt.utils.http.query(DETAILS['url']+'ping', decode_type='json', decode=True)
try:
return r['dict'].get('ret', False)
except Exception:
return False
def shutdown(opts):
'''
For this proxy shutdown is a no-op
'''
log.debug('rest_sample proxy shutdown() called...')
pass
.. _grains support code:
Grains are data about minions. Most proxied devices will have a paltry amount
of data as compared to a typical Linux server. Because proxy-minions are
started by a regular minion, they inherit a sizeable number of grain settings
which can be useful, especially when targeting (PYTHONPATH, for example).
of data as compared to a typical Linux server. By default, a proxy minion will
have no grains set at all. Salt core code requires values for ``kernel``,
``os``, and ``os_family``. To add them (and others) to your proxy minion for
a particular device, create a file in salt/grains named [proxytype].py and place
inside it the different functions that need to be run to collect the data you
are interested in. Here's an example:
All proxy minions set a grain called 'proxy'. If it is present, you know the
minion is controlling another device. To add more grains to your proxy minion
for a particular device, create a file in salt/grains named [proxytype].py and
place inside it the different functions that need to be run to collect the data
you are interested in. Here's an example:
.. code: python::
# -*- coding: utf-8 -*-
'''
Generate baseline proxy minion grains
'''
__proxyenabled__ = ['rest_sample']
__virtualname__ = 'rest_sample'
def __virtual__():
if 'proxy' not in __opts__:
return False
else:
return __virtualname__
def kernel():
return {'kernel':'proxy'}
def os():
return {'os':'proxy'}
def location():
return {'location': 'In this darn virtual machine. Let me out!'}
def os_family():
return {'os_family': 'proxy'}
def os_data():
return {'os_data': 'funkyHttp release 1.0.a.4.g'}
The __proxyenabled__ directive
------------------------------
Salt states and execution modules, by, and large, cannot "automatically" work
Salt execution moduless, by, and large, cannot "automatically" work
with proxied devices. Execution modules like ``pkg`` or ``sqlite3`` have no
meaning on a network switch or a housecat. For a state/execution module to be
meaning on a network switch or a housecat. For an execution module to be
available to a proxy-minion, the ``__proxyenabled__`` variable must be defined
in the module as an array containing the names of all the proxytypes that this
module can support. The array can contain the special value ``*`` to indicate
that the module supports all proxies.
If no ``__proxyenabled__`` variable is defined, then by default, the
state/execution module is unavailable to any proxy.
execution module is unavailable to any proxy.
Here is an excerpt from a module that was modified to support proxy-minions:
.. code-block:: python
__proxyenabled__ = ['*']
[...]
def ping():
if 'proxyobject' in __opts__:
if 'proxymodule' in __opts__:
if 'ping' in __opts__['proxyobject'].__attr__():
return __opts['proxyobject'].ping()
else:
@ -275,15 +446,18 @@ Here is an excerpt from a module that was modified to support proxy-minions:
else:
return True
And then in salt.proxy.junos we find
And then in salt.proxy.rest_sample.py we find
.. code-block:: python
def ping(self):
return self.connected
def ping():
'''
Is the REST server up?
'''
r = salt.utils.http.query(DETAILS['url']+'ping', decode_type='json', decode=True)
try:
return r['dict'].get('ret', False)
except Exception:
return False
The Junos API layer lacks the ability to do a traditional 'ping', so the
example simply checks the connection object field that indicates
if the ssh connection was successfully made to the device.

View File

@ -777,7 +777,7 @@ repository:
- git: master https://domain.com/pillar.git root=subdirectory
More information on the git external pillar can be found in the
:mod:`salt.pillar.get_pillar docs <salt.pillar.git_pillar>`.
:mod:`salt.pillar.git_pillar docs <salt.pillar.git_pillar>`.
.. _faq-gitfs-bug:

View File

@ -41,7 +41,12 @@ def __define_global_system_encoding_variable__():
# encoding. MS Windows has problems with this and reports the wrong
# encoding
import locale
encoding = locale.getdefaultlocale()[-1]
try:
encoding = locale.getdefaultlocale()[-1]
except ValueError:
# A bad locale setting was most likely found:
# https://github.com/saltstack/salt/issues/26063
pass
# This is now garbage collectable
del locale

View File

@ -417,8 +417,9 @@ class ProxyMinion(parsers.MinionOptionParser): # pylint: disable=no-init
'''
If sub-classed, run any shutdown operations on this method.
'''
if 'proxy' in self.minion.opts:
self.minion.opts['proxyobject'].shutdown(self.minion.opts)
if 'proxymodule' in self.minion.opts:
proxy_fn = self.minion.opts['proxymodule'].loaded_base_name + '.shutdown'
self.minion.opts['proxymodule'][proxy_fn](self.minion.opts)
logger.info('The proxy minion is shut down')

View File

@ -2129,7 +2129,9 @@ class Map(Cloud):
output[name] = self.create(
profile, local_master=local_master
)
if self.opts.get('show_deploy_args', False) is False and 'deploy_kwargs' in output:
if self.opts.get('show_deploy_args', False) is False \
and 'deploy_kwargs' in output \
and isinstance(output[name], dict):
output[name].pop('deploy_kwargs', None)
except SaltCloudException as exc:
log.error(

View File

@ -630,10 +630,10 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path):
for header_name, header_value in ret['headers'].items():
name = header_name.strip()
value = header_value.strip()
if name == 'Last-Modified'.lower():
if str(name).lower() == 'last-modified':
s3_file_mtime = datetime.datetime.strptime(
value, '%a, %d %b %Y %H:%M:%S %Z')
elif name == 'Content-Length'.lower():
elif str(name).lower() == 'content-length':
s3_file_size = int(value)
if (cached_file_size == s3_file_size and
cached_file_mtime > s3_file_mtime):

View File

@ -5,10 +5,14 @@ NOTE this is a little complicated--junos can only be accessed via salt-proxy-min
Thus, some grains make sense to get them from the minion (PYTHONPATH), but others
don't (ip_interfaces)
'''
import logging
__proxyenabled__ = ['junos']
__virtualname__ = 'junos'
log = logging.getLogger(__name__)
def __virtual__():
if 'proxy' not in __opts__:
@ -17,16 +21,31 @@ def __virtual__():
return __virtualname__
def location():
return {'location': 'dc-1-europe'}
def _remove_complex_types(dictionary):
'''
Linode-python is now returning some complex types that
are not serializable by msgpack. Kill those.
'''
for k, v in dictionary.iteritems():
if isinstance(v, dict):
dictionary[k] = _remove_complex_types(v)
elif hasattr(v, 'to_eng_string'):
dictionary[k] = v.to_eng_string()
return dictionary
def defaults():
return {'os': 'proxy', 'kernel': 'unknown', 'osrelease': 'proxy'}
def facts():
log.debug('----------- Trying to get facts')
facts = __opts__['proxymodule']['junos.facts']()
facts['version_info'] = 'override'
return facts
def os_family():
return {'os_family': 'junos'}
def os_data():
facts = {}
facts['version_info'] = {'major': '12,1', 'type': 'I', 'minor': '20131108_srx_12q1_x46_intgr', 'build': '0-613414'}
facts['os_family'] = 'proxy'
return facts

View File

@ -14,6 +14,14 @@ def __virtual__():
return __virtualname__
def kernel():
return {'kernel': 'proxy'}
def os():
return {'os': 'proxy'}
def location():
return {'location': 'In this darn virtual machine. Let me out!'}
@ -23,4 +31,4 @@ def os_family():
def os_data():
return __opts__['proxyobject'].grains()
return {'os_data': 'funkyHttp release 1.0.a.4.g'}

View File

@ -248,7 +248,7 @@ def engines(opts, functions, runners):
pack=pack)
def proxy(opts, functions, whitelist=None):
def proxy(opts, functions, whitelist=None, loaded_base_name=None):
'''
Returns the proxy module for this salt-proxy-minion
'''
@ -256,7 +256,8 @@ def proxy(opts, functions, whitelist=None):
opts,
tag='proxy',
whitelist=whitelist,
pack={'__proxy__': functions})
pack={'__proxy__': functions},
loaded_base_name=loaded_base_name)
def returners(opts, functions, whitelist=None, context=None):
@ -1138,14 +1139,17 @@ class LazyLoader(salt.utils.lazy.LazyDict):
# If this is a proxy minion then MOST modules cannot work. Therefore, require that
# any module that does work with salt-proxy-minion define __proxyenabled__ as a list
# containing the names of the proxy types that the module supports.
if not hasattr(mod, 'render') and 'proxy' in self.opts:
if not hasattr(mod, '__proxyenabled__') or \
self.opts['proxy']['proxytype'] in mod.__proxyenabled__ or \
'*' in mod.__proxyenabled__:
err_string = 'not a proxy_minion enabled module'
self.missing_modules[module_name] = err_string
self.missing_modules[name] = err_string
return False
#
# Render modules and state modules are OK though
if 'proxy' in self.opts:
if self.tag not in ['render', 'states']:
if not hasattr(mod, '__proxyenabled__') or \
(self.opts['proxy']['proxytype'] not in mod.__proxyenabled__ and
'*' not in mod.__proxyenabled__):
err_string = 'not a proxy_minion enabled module'
self.missing_modules[module_name] = err_string
self.missing_modules[name] = err_string
return False
if getattr(mod, '__load__', False) is not False:
log.info(

View File

@ -89,6 +89,8 @@ import salt.utils.schedule
import salt.utils.error
import salt.utils.zeromq
import salt.defaults.exitcodes
import salt.cli.daemons
from salt.defaults import DEFAULT_TARGET_DELIM
from salt.utils.debug import enable_sigusr1_handler
from salt.utils.event import tagify
@ -384,6 +386,7 @@ class SMinion(object):
self.utils = salt.loader.utils(self.opts)
self.functions = salt.loader.minion_mods(self.opts, utils=self.utils,
include_errors=True)
self.proxy = salt.loader.proxy(self.opts, None)
# TODO: remove
self.function_errors = {} # Keep the funcs clean
self.returners = salt.loader.returners(self.opts, self.functions)
@ -674,22 +677,22 @@ class Minion(MinionBase):
self.grains_cache = self.opts['grains']
if 'proxy' in self.opts['pillar']:
log.debug('I am {0} and I need to start some proxies for {1}'.format(self.opts['id'],
self.opts['pillar']['proxy']))
log.info('I am {0} and I need to start some proxies for {1}'.format(self.opts['id'],
self.opts['pillar']['proxy'].keys()))
for p in self.opts['pillar']['proxy']:
log.debug('Starting {0} proxy.'.format(p))
log.info('Starting {0} proxy.'.format(p))
pid = os.fork()
if pid > 0:
reinit_crypto()
continue
else:
reinit_crypto()
proxyminion = ProxyMinion(self.opts)
proxyminion = salt.cli.daemons.ProxyMinion(self.opts)
proxyminion.start(self.opts['pillar']['proxy'][p])
self.clean_die(signal.SIGTERM, None)
else:
log.debug('I am {0} and I am not supposed to start any proxies. '
'(Likely not a problem)'.format(self.opts['id']))
log.info('I am {0} and I am not supposed to start any proxies. '
'(Likely not a problem)'.format(self.opts['id']))
@tornado.gen.coroutine
def eval_master(self,
@ -2480,12 +2483,14 @@ class ProxyMinion(Minion):
This class instantiates a 'proxy' minion--a minion that does not manipulate
the host it runs on, but instead manipulates a device that cannot run a minion.
'''
def __init__(self, opts, timeout=60, safe=True): # pylint: disable=W0231
def __init__(self, opts, timeout=60, safe=True, loaded_base_name=None): # pylint: disable=W0231
'''
Pass in the options dict
'''
self._running = None
self.win_proc = []
self.loaded_base_name = loaded_base_name
# Warn if ZMQ < 3.2
if HAS_ZMQ:
try:
@ -2505,11 +2510,19 @@ class ProxyMinion(Minion):
)
# Late setup the of the opts grains, so we can log from the grains
# module
# print opts['proxymodule']
fq_proxyname = 'proxy.'+opts['proxy']['proxytype']
self.proxymodule = salt.loader.proxy(opts, fq_proxyname)
opts['proxyobject'] = self.proxymodule[opts['proxy']['proxytype']+'.Proxyconn'](opts['proxy'])
opts['id'] = opts['proxyobject'].id(opts)
opts['master'] = self.eval_master(opts,
timeout,
safe)
fq_proxyname = opts['proxy']['proxytype']
# Need to match the function signature of the other loader fns
# which is def proxy(opts, functions, whitelist=None, loaded_base_name=None)
# 'functions' for other loaders is a LazyLoader object
# but since we are not needing to merge functions into another fn dictionary
# we will pass 'None' in
self.proxymodule = salt.loader.proxy(opts, None, loaded_base_name=fq_proxyname)
opts['proxymodule'] = self.proxymodule
opts['grains'] = salt.loader.grains(opts)
opts['id'] = opts['proxymodule'][fq_proxyname+'.id'](opts)
opts.update(resolve_dns(opts))
self.opts = opts
self.opts['pillar'] = salt.pillar.get_pillar(
@ -2519,6 +2532,7 @@ class ProxyMinion(Minion):
opts['environment'],
pillarenv=opts.get('pillarenv'),
).compile_pillar()
opts['proxymodule'][fq_proxyname+'.init'](opts)
self.functions, self.returners, self.function_errors = self._load_modules()
self.serial = salt.payload.Serial(self.opts)
self.mod_opts = self._prep_mod_opts()
@ -2529,7 +2543,26 @@ class ProxyMinion(Minion):
self.opts,
self.functions,
self.returners)
# add default scheduling jobs to the minions scheduler
if 'mine.update' in self.functions:
log.info('Added mine.update to scheduler')
self.schedule.add_job({
'__mine_interval':
{
'function': 'mine.update',
'minutes': opts['mine_interval'],
'jid_include': True,
'maxrunning': 2
}
})
self.grains_cache = self.opts['grains']
# store your hexid to subscribe to zmq, hash since zmq filters are prefix
# matches this way we can avoid collisions
self.hexid = hashlib.sha1(self.opts['id']).hexdigest()
# self._running = True
def _prep_mod_opts(self):

View File

@ -772,7 +772,8 @@ def version(name, check_remote=False, source=None, pre_versions=False):
log.error(err)
raise CommandExecutionError(err)
if _LooseVersion(chocolatey_version()) >= _LooseVersion('0.9.9'):
use_list = _LooseVersion(chocolatey_version()) >= _LooseVersion('0.9.9')
if use_list:
choco_cmd = "list"
else:
choco_cmd = "version"
@ -794,18 +795,22 @@ def version(name, check_remote=False, source=None, pre_versions=False):
ret = {}
res = result['stdout'].split('\n')
if use_list:
res = res[:-1]
# the next bit is to deal with the stupid default PowerShell formatting.
# printing two value pairs is shown in columns, whereas printing six
# pairs is shown in rows...
if not salt.utils.is_true(check_remote):
ver_re = re.compile(r'(\S+)\s+(.+)')
for line in result['stdout'].split('\n')[:-1]:
for line in res:
for name, ver in ver_re.findall(line):
ret['name'] = name
ret['found'] = ver
else:
ver_re = re.compile(r'(\S+)\s+:\s*(.*)')
for line in result['stdout'].split('\n')[:-1]:
for line in res:
for key, value in ver_re.findall(line):
ret[key] = value

View File

@ -3942,9 +3942,9 @@ def manage_file(name,
if not sfn:
return _error(
ret, 'Source file {0!r} not found'.format(source))
# If the downloaded file came from a non salt server source verify
# that it matches the intended sum value
if _urlparse(source).scheme != 'salt':
# If the downloaded file came from a non salt server or local source
# verify that it matches the intended sum value
if _urlparse(source).scheme not in ('salt', ''):
dl_sum = get_hash(sfn, source_sum['hash_type'])
if dl_sum != source_sum['hsum']:
ret['comment'] = ('File sum set for file {0} of {1} does '

View File

@ -37,8 +37,8 @@ def __execute_cmd(name, xml):
if not os.path.isdir(tmp_dir):
os.mkdir(tmp_dir)
with tempfile.NamedTemporaryFile(dir=tmp_dir,
prefix=name,
suffix=os.getpid(),
prefix=name+str(os.getpid()),
suffix='.xml',
delete=False) as fh:
tmpfilename = fh.name
fh.write(xml)

View File

@ -11,7 +11,7 @@ from __future__ import absolute_import
import logging
# Juniper interface libraries
# https://github.com/jeremyschulman/py-junos-eznc
# https://github.com/Juniper/py-junos-eznc
try:
@ -28,6 +28,7 @@ except ImportError:
# Set up logging
log = logging.getLogger(__name__)
# Define the module's virtual name
__virtualname__ = 'junos'
@ -37,7 +38,7 @@ __proxyenabled__ = ['junos']
def __virtual__():
'''
We need the Junos adapter libraries for this
module to work. We also need a proxyobject object
module to work. We also need a proxymodule entry in __opts__
in the opts dictionary
'''
if HAS_JUNOS and 'proxy' in __opts__:
@ -52,13 +53,17 @@ def facts_refresh():
if the device configuration is changed by some other actor.
'''
return __opts__['proxyobject'].refresh
return __opts__['proxymodule']['junos.refresh']()
def call_rpc():
return __opts__['proxymodule']['junos.rpc']()
def set_hostname(hostname=None, commit_change=True):
conn = __opts__['proxymodule']['junos.conn']()
ret = dict()
conn = __opts__['proxyobject']
if hostname is None:
ret['out'] = False
return ret
@ -79,8 +84,7 @@ def set_hostname(hostname=None, commit_change=True):
def commit():
conn = __opts__['proxyobject']
conn = __opts__['proxymodule']['junos.conn']()
ret = {}
commit_ok = conn.cu.commit_check()
if commit_ok:
@ -99,8 +103,8 @@ def commit():
def rollback():
conn = __opts__['proxyobject']
ret = dict()
conn = __opts__['proxymodule']['junos.conn']()
ret['out'] = conn.cu.rollback(0)
@ -114,8 +118,8 @@ def rollback():
def diff():
conn = __opts__['proxymodule']['junos.conn']()
ret = dict()
conn = __opts__['proxyobject']
ret['out'] = True
ret['message'] = conn.cu.diff()
@ -124,7 +128,7 @@ def diff():
def ping():
conn = __opts__['proxymodule']['junos.conn']()
ret = dict()
conn = __opts__['proxyobject']
ret['message'] = conn.cli('show system uptime')
ret['message'] = conn.probe()
ret['out'] = True

View File

@ -884,8 +884,17 @@ def remove(name=None,
except MinionError as exc:
raise CommandExecutionError(exc)
old = list_pkgs(jail=jail, chroot=chroot)
targets = [x for x in pkg_params if x in old]
targets = []
old = list_pkgs(jail=jail, chroot=chroot, with_origin=True)
for pkg in pkg_params.items():
# FreeBSD pkg supports `openjdk` and `java/openjdk7` package names
if pkg[0].find("/") > 0:
origin = pkg[0]
pkg = [k for k, v in old.iteritems() if v['origin'] == origin][0]
if pkg[0] in old:
targets.append(pkg[0])
if not targets:
return {}
@ -915,7 +924,7 @@ def remove(name=None,
__salt__['cmd.run'](cmd, python_shell=False, output_loglevel='trace')
__context__.pop(_contextkey(jail, chroot), None)
__context__.pop(_contextkey(jail, chroot, prefix='pkg.origin'), None)
new = list_pkgs(jail=jail, chroot=chroot)
new = list_pkgs(jail=jail, chroot=chroot, with_origin=True)
return salt.utils.compare_dicts(old, new)
# Support pkg.delete to remove packages, since this is the CLI usage

View File

@ -17,11 +17,11 @@ __virtualname__ = 'pkg'
def __virtual__():
'''
Only work on RestExampleOS
Only work on proxy
'''
# Enable on these platforms only.
enable = set((
'RestExampleOS',
'proxy',
))
if __grains__['os'] in enable:
return __virtualname__
@ -29,16 +29,16 @@ def __virtual__():
def list_pkgs(versions_as_list=False, **kwargs):
return __opts__['proxyobject'].package_list()
return __opts__['proxymodule']['rest_sample.package_list']()
def install(name=None, refresh=False, fromrepo=None,
pkgs=None, sources=None, **kwargs):
return __opts__['proxyobject'].package_install(name, **kwargs)
return __opts__['proxymodule']['rest_sample.package_install'](name, **kwargs)
def remove(name=None, pkgs=None, **kwargs):
return __opts__['proxyobject'].package_remove(name)
return __opts__['proxymodule']['rest_sample.package_remove'](name)
def version(*names, **kwargs):
@ -55,7 +55,7 @@ def version(*names, **kwargs):
salt '*' pkg.version <package1> <package2> <package3> ...
'''
if len(names) == 1:
return str(__opts__['proxyobject'].package_status(names))
return str(__opts__['proxymodule']['rest_sample.package_status'](names))
def installed(
@ -68,7 +68,7 @@ def installed(
sources=None,
**kwargs):
p = __opts__['proxyobject'].package_status(name)
p = __opts__['proxymodule']['rest_sample.package_status'](name)
if version is None:
if 'ret' in p:
return str(p['ret'])

View File

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
'''
Provide the service module for the proxy-minion REST sample
'''
# Import python libs
from __future__ import absolute_import
import logging
__proxyenabled__ = ['rest_sample']
log = logging.getLogger(__name__)
__func_alias__ = {
'reload_': 'reload'
}
# Define the module's virtual name
__virtualname__ = 'service'
def __virtual__():
'''
Only work on systems that are a proxy minion
'''
if __grains__['kernel'] == 'proxy':
return __virtualname__
return False
def get_all():
'''
Return a list of all available services
CLI Example:
.. code-block:: bash
salt '*' service.get_all
'''
proxy_fn = 'rest_sample'+ '.service_list'
return __opts__['proxymodule'][proxy_fn]()
def start(name):
'''
Start the specified service on the rest_sample
CLI Example:
.. code-block:: bash
salt '*' service.start <service name>
'''
proxy_fn = 'rest_sample'+ '.service_start'
return __opts__['proxymodule'][proxy_fn](name)
def stop(name):
'''
Stop the specified service on the rest_sample
CLI Example:
.. code-block:: bash
salt '*' service.stop <service name>
'''
proxy_fn = 'rest_sample'+ '.service_stop'
return __opts__['proxymodule'][proxy_fn](name)
def restart(name):
'''
Restart the specified service with rest_sample
CLI Example:
.. code-block:: bash
salt '*' service.restart <service name>
'''
proxy_fn = 'rest_sample'+ '.service_restart'
return __opts__['proxymodule'][proxy_fn](name)
def status(name, sig):
'''
Return the status for a service via rest_sample, returns a bool
whether the service is running.
CLI Example:
.. code-block:: bash
salt '*' service.status <service name>
'''
proxy_fn = 'rest_sample' + '.service_status'
resp = __opts__['proxymodule'][proxy_fn](name)
if resp['comment'] == 'stopped':
return {name: False}
if resp['comment'] == 'running':
return {name: True}

View File

@ -111,8 +111,9 @@ def ping():
salt '*' test.ping
'''
if 'proxyobject' in __opts__:
return __opts__['proxyobject'].ping()
if 'proxymodule' in __opts__:
ping_cmd = __opts__['proxymodule'].loaded_base_name + '.ping'
return __opts__['proxymodule'][ping_cmd]()
else:
return True

View File

@ -31,6 +31,23 @@ def __virtual__():
return False
def _get_dirs(user_dir, startup_dir):
'''
Return a list of startup dirs
'''
try:
users = os.listdir(user_dir)
except WindowsError: # pylint: disable=E0602
users = []
full_dirs = []
for user in users:
full_dir = os.path.join(user_dir, user, startup_dir)
if os.path.exists(full_dir):
full_dirs.append(full_dir)
return full_dirs
def list_():
'''
Get a list of automatically running programs
@ -56,21 +73,18 @@ def list_():
autoruns[key].append(line)
# Find autoruns in user's startup folder
if os.path.exists('C:\\Documents and Settings\\'):
user_dir = 'C:\\Documents and Settings\\'
startup_dir = '\\Start Menu\\Programs\\Startup'
else:
user_dir = 'C:\\Documents and Settings\\'
startup_dir = '\\Start Menu\\Programs\\Startup'
full_dirs = _get_dirs(user_dir, startup_dir)
if not full_dirs:
user_dir = 'C:\\Users\\'
startup_dir = '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup'
full_dirs = _get_dirs(user_dir, startup_dir)
for user in os.listdir(user_dir):
try:
full_dir = user_dir + user + startup_dir
files = os.listdir(full_dir)
autoruns[full_dir] = []
for afile in files:
autoruns[full_dir].append(afile)
except Exception:
pass
for full_dir in full_dirs:
files = os.listdir(full_dir)
autoruns[full_dir] = []
for single_file in files:
autoruns[full_dir].append(single_file)
return autoruns

View File

@ -7,58 +7,79 @@ Interface with a Junos device via proxy-minion.
from __future__ import print_function
from __future__ import absolute_import
import logging
# Import 3rd-party libs
import jnpr.junos
import jnpr.junos.utils
import jnpr.junos.cfg
# import jnpr.junos
# import jnpr.junos.utils
# import jnpr.junos.utils.config
import json
HAS_JUNOS = True
__proxyenabled__ = ['junos']
thisproxy = {}
class Proxyconn(object):
log = logging.getLogger(__name__)
# def __init__(opts):
# '''
# Open the connection to the Junos device, login, and bind to the
# Resource class
# '''
# log.debug('Opening connection to junos')
# thisproxy['conn'] = jnpr.junos.Device(user=opts['proxy']['username'],
# host=opts['proxy']['host'],
# password=opts['proxy']['passwd'])
# thisproxy['conn'].open()
# thisproxy['conn'].bind(cu=jnpr.junos.utils.config.Config)
def conn():
return thisproxy['conn']
def facts():
return thisproxy['conn'].facts
def refresh():
return thisproxy['conn'].facts_refresh()
def proxytype():
'''
This class provides the persistent connection to the device that is being
controlled.
Returns the name of this proxy
'''
return 'junos'
def id(opts):
'''
Returns a unique ID for this proxy minion
'''
return thisproxy['conn'].facts['hostname']
def ping():
'''
Ping? Pong!
'''
return thisproxy['conn'].connected
def shutdown(opts):
'''
This is called when the proxy-minion is exiting to make sure the
connection to the device is closed cleanly.
'''
def __init__(self, details):
'''
Open the connection to the Junos device, login, and bind to the
Resource class
'''
self.conn = jnpr.junos.Device(user=details['username'],
host=details['host'],
password=details['passwd'])
self.conn.open()
self.conn.bind(cu=jnpr.junos.cfg.Resource)
log.debug('Proxy module {0} shutting down!!'.format(opts['id']))
try:
thisproxy['conn'].close()
except Exception:
pass
def proxytype(self):
'''
Returns the name of this proxy
'''
return 'junos'
def id(self, opts):
'''
Returns a unique ID for this proxy minion
'''
return self.conn.facts['hostname']
def ping(self):
'''
Ping? Pong!
'''
return self.conn.connected
def shutdown(self, opts):
'''
This is called when the proxy-minion is exiting to make sure the
connection to the device is closed cleanly.
'''
print('Proxy module {0} shutting down!!'.format(opts['id']))
try:
self.conn.close()
except Exception:
pass
def rpc():
return json.dumps(thisproxy['conn'].rpc.get_software_information())

View File

@ -1,147 +1,173 @@
# -*- coding: utf-8 -*-
'''
This is a simple proxy-minion designed to connect to and communicate with
the bottle-based web service contained in salt/tests/rest.py.
Note this example needs the 'requests' library.
Requests is not a hard dependency for Salt
the bottle-based web service contained in https://github.com/salt-contrib/proxyminion_rest_example
'''
from __future__ import absolute_import
# Import python libs
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
import logging
import salt.utils.http
HAS_REST_EXAMPLE = True
# This must be present or the Salt loader won't load this module
__proxyenabled__ = ['rest_sample']
# Variables are scoped to this module so we can have persistent data
# across calls to fns in here.
GRAINS_CACHE = {}
DETAILS = {}
# Want logging!
log = logging.getLogger(__file__)
# This does nothing, it's here just as an example and to provide a log
# entry when the module is loaded.
def __virtual__():
'''
Only return if all the modules are available
'''
if not HAS_REQUESTS:
log.debug('rest_sample proxy __virtual__() called...')
return True
def init(opts):
'''
Every proxy module needs an 'init', though you can
just put a 'pass' here if it doesn't need to do anything.
'''
log.debug('rest_sample proxy init() called...')
# Save the REST URL
DETAILS['url'] = opts['proxy']['url']
# Make sure the REST URL ends with a '/'
if not DETAILS['url'].endswith('/'):
DETAILS['url'] += '/'
def id(opts):
'''
Return a unique ID for this proxy minion. This ID MUST NOT CHANGE.
If it changes while the proxy is running the salt-master will get
really confused and may stop talking to this minion
'''
r = salt.utils.http.query(opts['proxy']['url']+'id', decode_type='json', decode=True)
return r['dict']['id'].encode('ascii', 'ignore')
def grains():
'''
Get the grains from the proxied device
'''
if not GRAINS_CACHE:
r = salt.utils.http.query(DETAILS['url']+'info', decode_type='json', decode=True)
GRAINS_CACHE = r['dict']
return GRAINS_CACHE
def grains_refresh():
'''
Refresh the grains from the proxied device
'''
GRAINS_CACHE = {}
return grains()
def service_start(name):
'''
Start a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/start/'+name, decode_type='json', decode=True)
return r['dict']
def service_stop(name):
'''
Stop a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/stop/'+name, decode_type='json', decode=True)
return r['dict']
def service_restart(name):
'''
Restart a "service" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/restart/'+name, decode_type='json', decode=True)
return r['dict']
def service_list():
'''
List "services" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/list', decode_type='json', decode=True)
return r['dict']
def service_status(name):
'''
Check if a service is running on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'service/status/'+name, decode_type='json', decode=True)
return r['dict']
def package_list():
'''
List "packages" installed on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/list', decode_type='json', decode=True)
return r['dict']
def package_install(name, **kwargs):
'''
Install a "package" on the REST server
'''
cmd = DETAILS['url']+'package/install/'+name
if 'version' in kwargs:
cmd += '/'+kwargs['version']
else:
cmd += '/1.0'
r = salt.utils.http.query(cmd, decode_type='json', decode=True)
return r['dict']
def package_remove(name):
'''
Remove a "package" on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/remove/'+name, decode_type='json', decode=True)
return r['dict']
def package_status(name):
'''
Check the installation status of a package on the REST server
'''
r = salt.utils.http.query(DETAILS['url']+'package/status/'+name, decode_type='json', decode=True)
return r['dict']
def ping():
'''
Is the REST server up?
'''
r = salt.utils.http.query(DETAILS['url']+'ping', decode_type='json', decode=True)
try:
return r['dict'].get('ret', False)
except Exception:
return False
class Proxyconn(object):
def shutdown(opts):
'''
Interface with the REST sample web service (rest.py at
https://github.com/cro/salt-proxy-rest)
For this proxy shutdown is a no-op
'''
def __init__(self, details):
self.url = details['url']
self.grains_cache = {}
def id(self, opts):
'''
Return a unique ID for this proxy minion
'''
r = requests.get(self.url+'id')
return r.text.encode('ascii', 'ignore')
def grains(self):
'''
Get the grains from the proxied device
'''
if not self.grains_cache:
r = requests.get(self.url+'info')
self.grains_cache = r.json()
return self.grains_cache
def grains_refresh(self):
'''
Refresh the grains from the proxied device
'''
self.grains_cache = {}
return self.grains()
def service_start(self, name):
'''
Start a "service" on the REST server
'''
r = requests.get(self.url+'service/start/'+name)
return r.json()
def service_stop(self, name):
'''
Stop a "service" on the REST server
'''
r = requests.get(self.url+'service/stop/'+name)
return r.json()
def service_restart(self, name):
'''
Restart a "service" on the REST server
'''
r = requests.get(self.url+'service/restart/'+name)
return r.json()
def service_list(self):
'''
List "services" on the REST server
'''
r = requests.get(self.url+'service/list')
return r.json()
def service_status(self, name):
'''
Check if a service is running on the REST server
'''
r = requests.get(self.url+'service/status/'+name)
return r.json()
def package_list(self):
'''
List "packages" installed on the REST server
'''
r = requests.get(self.url+'package/list')
return r.json()
def package_install(self, name, **kwargs):
'''
Install a "package" on the REST server
'''
cmd = self.url+'package/install/'+name
if 'version' in kwargs:
cmd += '/'+kwargs['version']
else:
cmd += '/1.0'
r = requests.get(cmd)
def package_remove(self, name):
'''
Remove a "package" on the REST server
'''
r = requests.get(self.url+'package/remove/'+name)
return r.json()
def package_status(self, name):
'''
Check the installation status of a package on the REST server
'''
r = requests.get(self.url+'package/status/'+name)
return r.json()
def ping(self):
'''
Is the REST server up?
'''
r = requests.get(self.url+'ping')
try:
if r.status_code == 200:
return True
else:
return False
except Exception:
return False
def shutdown(self, opts):
'''
For this proxy shutdown is a no-op
'''
pass
log.debug('rest_sample proxy shutdown() called...')

View File

@ -182,6 +182,8 @@ def _find_remove_targets(name=None,
Inspect the arguments to pkg.removed and discover what packages need to
be removed. Return a dict of packages to remove.
'''
if __grains__['os'] == 'FreeBSD':
kwargs['with_origin'] = True
cur_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs)
if pkgs:
to_remove = _repack_pkgs(pkgs, normalize=normalize)
@ -204,7 +206,14 @@ def _find_remove_targets(name=None,
targets = []
problems = []
for pkgname, pkgver in six.iteritems(to_remove):
cver = cur_pkgs.get(pkgname, [])
# FreeBSD pkg supports `openjdk` and `java/openjdk7` package names
origin = bool(re.search('/', pkgname))
if __grains__['os'] == 'FreeBSD' and origin:
cver = [k for k, v in six.iteritems(cur_pkgs) if v['origin'] == pkgname]
else:
cver = cur_pkgs.get(pkgname, [])
# Package not installed, no need to remove
if not cver:
continue
@ -279,6 +288,9 @@ def _find_install_targets(name=None,
else:
ignore_types = []
if __grains__['os'] == 'FreeBSD':
kwargs['with_origin'] = True
cur_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs)
if any((pkgs, sources)):
if pkgs:
@ -319,7 +331,14 @@ def _find_install_targets(name=None,
to_unpurge = _find_unpurge_targets(desired)
cver = cur_pkgs.get(name, [])
# FreeBSD pkg supports `openjdk` and `java/openjdk7` package names
origin = bool(re.search('/', name))
if __grains__['os'] == 'FreeBSD' and origin:
cver = [k for k, v in six.iteritems(cur_pkgs) if v['origin'] == name]
else:
cver = cur_pkgs.get(name, [])
if name not in to_unpurge:
if version and version in cver and not pkg_verify:
# The package is installed and is the correct version
@ -477,8 +496,15 @@ def _verify_install(desired, new_pkgs):
'''
ok = []
failed = []
for pkgname, pkgver in six.iteritems(desired):
cver = new_pkgs.get(pkgname)
for pkgname, pkgver in desired.items():
# FreeBSD pkg supports `openjdk` and `java/openjdk7` package names
origin = bool(re.search('/', pkgname))
if __grains__['os'] == 'FreeBSD' and origin:
cver = [k for k, v in six.iteritems(new_pkgs) if v['origin'] == pkgname]
else:
cver = new_pkgs.get(pkgname)
if not cver:
failed.append(pkgname)
continue
@ -1118,6 +1144,8 @@ def installed(
and x not in to_reinstall]
failed = [x for x in targets if x not in modified]
else:
if __grains__['os'] == 'FreeBSD':
kwargs['with_origin'] = True
ok, failed = \
_verify_install(
desired, __salt__['pkg.list_pkgs'](

View File

@ -162,7 +162,12 @@ def managed(name,
use_vt=use_vt,
)
ret['result'] = _ret['retcode'] == 0
if _ret['retcode'] != 0:
ret['result'] = False
ret['comment'] = _ret['stdout'] + _ret['stderr']
return ret
ret['result'] = True
ret['changes']['new'] = __salt__['cmd.run_stderr'](
'{0} -V'.format(venv_py)).strip('\n')

View File

@ -311,6 +311,8 @@ def query(url,
urllib_request.HTTPHandler,
urllib_request.HTTPCookieProcessor(sess_cookies)
]
if password_mgr:
handlers.append(urllib_request.HTTPBasicAuthHandler(password_mgr))
if url.startswith('https') or port == 443:
hostname = request.get_host()

View File

@ -121,6 +121,6 @@ class WheelClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object):
>>> wheel.cmd('key.finger', ['jerry'])
{'minions': {'jerry': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}}
'''
return self.low(fun, kwarg)
Wheel = WheelClient # for backward-compat

View File

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.modules import rest_package
# Globals
rest_package.__opts__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RestPkgTestCase(TestCase):
'''
Test cases for salt.modules.rest_package
'''
def test_list_pkgs(self):
'''
Test for list pkgs
'''
with patch.dict(rest_package.__opts__, {'proxyobject': MagicMock()}):
self.assertTrue(rest_package.list_pkgs())
def test_install(self):
'''
Test for install
'''
with patch.dict(rest_package.__opts__, {'proxyobject': MagicMock()}):
self.assertTrue(rest_package.install())
def test_remove(self):
'''
Test for remove
'''
with patch.dict(rest_package.__opts__, {'proxyobject': MagicMock()}):
self.assertTrue(rest_package.remove())
def test_version(self):
'''
Test to return a string representing the package version or
an empty string if not installed.
'''
with patch.dict(rest_package.__opts__, {'proxyobject': MagicMock()}):
self.assertTrue(rest_package.version('A'))
def test_installed(self):
'''
Test for installed
'''
with patch.dict(rest_package.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_package.__opts__['proxyobject'],
'package_status',
MagicMock(return_value={'ret': 'ret'})):
self.assertEqual(rest_package.installed('name'), 'ret')
self.assertTrue(rest_package.installed('name'))
self.assertFalse(rest_package.installed('name', version='v'))
if __name__ == '__main__':
from integration import run_tests
run_tests(RestPkgTestCase, needs_daemon=False)

View File

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
'''
# Import Python libs
from __future__ import absolute_import
# Import Salt Testing Libs
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import Salt Libs
from salt.modules import rest_service
# Globals
rest_service.__opts__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class RestSvcTestCase(TestCase):
'''
Test cases for salt.modules.rest_service
'''
def test_start(self):
'''
Test to start the specified service
'''
with patch.dict(rest_service.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_service.__opts__['proxyobject'],
'service_start', MagicMock(return_value=True)):
self.assertTrue(rest_service.start('name'))
def test_stop(self):
'''
Test to stop the specified service
'''
with patch.dict(rest_service.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_service.__opts__['proxyobject'],
'service_stop', MagicMock(return_value=True)):
self.assertTrue(rest_service.stop('name'))
def test_restart(self):
'''
Test to restart the named service
'''
with patch.dict(rest_service.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_service.__opts__['proxyobject'],
'service_restart', MagicMock(return_value=True)):
self.assertTrue(rest_service.restart('name'))
def test_status(self):
'''
Test to return the status for a service, returns a bool whether
the service is running.
'''
with patch.dict(rest_service.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_service.__opts__['proxyobject'],
'service_status', MagicMock(return_value=True)):
self.assertTrue(rest_service.status('name'))
def test_list_(self):
'''
Test for list services.
'''
with patch.dict(rest_service.__opts__, {'proxyobject': MagicMock()}):
with patch.object(rest_service.__opts__['proxyobject'],
'service_list_', MagicMock(return_value=True)):
self.assertTrue(rest_service.list_())
if __name__ == '__main__':
from integration import run_tests
run_tests(RestSvcTestCase, needs_daemon=False)