mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #26135 from cro/pm20155_2
Fix proxy minions in 2015.5 and significantly update documentation.
This commit is contained in:
commit
2b8dcce0ca
14 changed files with 685 additions and 448 deletions
|
@ -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.
|
||||
|
|
|
@ -415,8 +415,9 @@ class ProxyMinion(parsers.MinionOptionParser):
|
|||
'''
|
||||
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')
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -207,7 +207,7 @@ def raw_mod(opts, name, functions, mod='modules'):
|
|||
return dict(loader._dict) # return a copy of *just* the funcs for `name`
|
||||
|
||||
|
||||
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
|
||||
'''
|
||||
|
@ -216,6 +216,7 @@ def proxy(opts, functions, whitelist=None):
|
|||
tag='proxy',
|
||||
whitelist=whitelist,
|
||||
pack={'__proxy__': functions},
|
||||
loaded_base_name=loaded_base_name
|
||||
)
|
||||
|
||||
|
||||
|
@ -1032,14 +1033,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(
|
||||
|
|
|
@ -82,6 +82,7 @@ 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.ext.six import string_types
|
||||
|
@ -334,7 +335,10 @@ class SMinion(object):
|
|||
self.opts['environment'],
|
||||
pillarenv=self.opts.get('pillarenv'),
|
||||
).compile_pillar()
|
||||
self.functions = salt.loader.minion_mods(self.opts, include_errors=True)
|
||||
self.utils = salt.loader.utils(self.opts)
|
||||
self.functions = salt.loader.minion_mods(self.opts,
|
||||
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)
|
||||
|
@ -736,22 +740,21 @@ class Minion(MinionBase):
|
|||
# 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()
|
||||
|
||||
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:
|
||||
continue
|
||||
else:
|
||||
proxyminion = salt.ProxyMinion()
|
||||
proxyminion = salt.cli.daemons.ProxyMinion()
|
||||
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']))
|
||||
|
||||
# __init__() from MinionBase is called in Minion.eval_master()
|
||||
|
||||
|
@ -2897,12 +2900,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:
|
||||
|
@ -2922,11 +2927,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.authenticate(timeout, safe)
|
||||
|
@ -2937,6 +2950,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()
|
||||
|
@ -2947,7 +2961,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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'])
|
||||
|
|
105
salt/modules/system_rest_sample.py
Normal file
105
salt/modules/system_rest_sample.py
Normal 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}
|
|
@ -105,8 +105,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
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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...')
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
Loading…
Add table
Reference in a new issue