mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
doc update
This commit is contained in:
parent
8cead792f0
commit
7f81bca162
6 changed files with 114 additions and 107 deletions
|
@ -400,6 +400,7 @@ execution modules
|
|||
rest_sample_utils
|
||||
rest_service
|
||||
restartcheck
|
||||
restconf
|
||||
ret
|
||||
rh_ip
|
||||
rh_service
|
||||
|
|
|
@ -31,5 +31,6 @@ proxy modules
|
|||
panos
|
||||
philips_hue
|
||||
rest_sample
|
||||
restconf
|
||||
ssh_sample
|
||||
vcenter
|
||||
|
|
|
@ -277,6 +277,7 @@ state modules
|
|||
rdp
|
||||
redismod
|
||||
reg
|
||||
restconf
|
||||
rsync
|
||||
rvm
|
||||
salt_proxy
|
||||
|
|
|
@ -1,36 +1,32 @@
|
|||
'''
|
||||
"""
|
||||
Execution module for Restconf Proxy minions
|
||||
|
||||
:codeauthor: Jamie (Bear) Murphy <jamiemurphyit@gmail.com>
|
||||
:maturity: new
|
||||
:platform: any
|
||||
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
import six # noqa: F401
|
||||
import salt.utils # noqa: F401
|
||||
"""
|
||||
|
||||
__proxyenabled__ = ["restconf"]
|
||||
__virtualname__ = 'restconf'
|
||||
__virtualname__ = "restconf"
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if __opts__.get("proxy", {}).get("proxytype") != __virtualname__: # noqa: F821
|
||||
return False, "Proxytype does not match: {0}".format(__virtualname__)
|
||||
if __opts__.get("proxy", {}).get("proxytype") != __virtualname__: # noqa: F821
|
||||
return False, "Proxytype does not match: {}".format(__virtualname__)
|
||||
return True
|
||||
|
||||
|
||||
def info():
|
||||
'''
|
||||
"""
|
||||
Should return some quick state info of the restconf device?
|
||||
'''
|
||||
"""
|
||||
return "Hello i am a restconf module"
|
||||
|
||||
|
||||
def get_data(uri):
|
||||
return __proxy__['restconf.request'](uri) # noqa: F821
|
||||
return __proxy__["restconf.request"](uri) # noqa: F821
|
||||
|
||||
|
||||
def set_data(uri, method, dict_payload):
|
||||
return __proxy__['restconf.request'](uri, method, dict_payload) # noqa: F821
|
||||
return __proxy__["restconf.request"](uri, method, dict_payload) # noqa: F821
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'''
|
||||
"""
|
||||
Proxy Minion to manage Restconf Devices
|
||||
|
||||
:codeauthor: Jamie (Bear) Murphy <jamiemurphyit@gmail.com>
|
||||
|
@ -10,7 +10,7 @@ Usage
|
|||
|
||||
.. note::
|
||||
|
||||
To be able to use this module you need to enable to RESTCONF on your device
|
||||
To be able to use this module you need to enable to RESTCONF on your device
|
||||
and having https enabled.
|
||||
|
||||
Cisco Configuration example:
|
||||
|
@ -23,7 +23,7 @@ Usage
|
|||
|
||||
.. note::
|
||||
|
||||
RESTCONF requires modern OS distributions.
|
||||
RESTCONF requires modern OS distributions.
|
||||
This plugin has been written specifically to use JSON Restconf endpoints
|
||||
|
||||
Pillar
|
||||
|
@ -72,19 +72,18 @@ Proxy Pillar Example
|
|||
username: example
|
||||
password: example
|
||||
verify: false
|
||||
'''
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
"""
|
||||
|
||||
# Import python stdlib
|
||||
import copy
|
||||
import logging
|
||||
import salt.utils.http
|
||||
import json
|
||||
import logging
|
||||
|
||||
import salt.utils.http
|
||||
|
||||
# Import Salt modules
|
||||
from salt.exceptions import SaltException
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# proxy properties
|
||||
# -----------------------------------------------------------------------------
|
||||
|
@ -96,7 +95,7 @@ __proxyenabled__ = ["restconf"]
|
|||
# globals
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
__virtualname__ = 'restconf'
|
||||
__virtualname__ = "restconf"
|
||||
log = logging.getLogger(__file__)
|
||||
restconf_device = {}
|
||||
|
||||
|
@ -106,12 +105,13 @@ restconf_device = {}
|
|||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
"""
|
||||
This Proxy Module is widely available as there are no external dependencies.
|
||||
'''
|
||||
"""
|
||||
log.debug("restconf proxy __virtual__() called...")
|
||||
return __virtualname__
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# proxy functions
|
||||
# -----------------------------------------------------------------------------
|
||||
|
@ -119,53 +119,54 @@ def __virtual__():
|
|||
|
||||
def init(opts):
|
||||
log.debug("restconf proxy init(opts) called...")
|
||||
# restconf/data/ietf-yang-library:modules-state/module
|
||||
'''
|
||||
Open the connection to the RESTCONF Device.
|
||||
# Open the connection to the RESTCONF Device.
|
||||
# As the communication is HTTP based, there is no connection to maintain,
|
||||
# however, in order to test the connectivity and make sure we are able to
|
||||
# bring up this Minion, we are checking the standard restconf state uri.
|
||||
|
||||
As the communication is HTTP based, there is no connection to maintain,
|
||||
however, in order to test the connectivity and make sure we are able to
|
||||
bring up this Minion, we are checking the standard restconf state uri.
|
||||
'''
|
||||
proxy_dict = opts.get('proxy', {})
|
||||
proxy_dict = opts.get("proxy", {})
|
||||
conn_args = copy.deepcopy(proxy_dict)
|
||||
conn_args.pop('proxytype', None)
|
||||
opts['multiprocessing'] = conn_args.pop('multiprocessing', True)
|
||||
conn_args.pop("proxytype", None)
|
||||
opts["multiprocessing"] = conn_args.pop("multiprocessing", True)
|
||||
# This is not a SSH-based proxy, so it should be safe to enable
|
||||
# multiprocessing.
|
||||
restconf_device['conn_args'] = conn_args
|
||||
restconf_device["conn_args"] = conn_args
|
||||
try:
|
||||
response = connection_test()
|
||||
if response[0]:
|
||||
# Execute a very simple command to confirm we are able to connect properly
|
||||
restconf_device['initialized'] = True
|
||||
restconf_device['up'] = True
|
||||
log.info('Connected to %s', conn_args['hostname'], exc_info=True)
|
||||
restconf_device["initialized"] = True
|
||||
restconf_device["up"] = True
|
||||
log.info("Connected to %s", conn_args["hostname"], exc_info=True)
|
||||
|
||||
else:
|
||||
restconf_device['initialized'] = False
|
||||
restconf_device['up'] = False
|
||||
log.error('Unable to connect to %s', conn_args['hostname'], exc_info=True)
|
||||
restconf_device["initialized"] = False
|
||||
restconf_device["up"] = False
|
||||
log.error("Unable to connect to %s", conn_args["hostname"], exc_info=True)
|
||||
except SaltException:
|
||||
log.error('Unable to connect to %s', conn_args['hostname'], exc_info=True)
|
||||
log.error("Unable to connect to %s", conn_args["hostname"], exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def connection_test():
|
||||
log.debug("restconf proxy connection_test() called...")
|
||||
response = salt.utils.http.query(
|
||||
"https://{h}/restconf/yang-library-version".format(h=restconf_device['conn_args']['hostname']),
|
||||
method='GET',
|
||||
decode_type="json",
|
||||
decode=True,
|
||||
verify_ssl=restconf_device['conn_args']['verify'],
|
||||
username=restconf_device['conn_args']['username'],
|
||||
password=restconf_device['conn_args']['password'],
|
||||
header_list=['Accept: application/yang-data+json', 'Content-Type: application/yang-data+json']
|
||||
|
||||
)
|
||||
"https://{h}/restconf/yang-library-version".format(
|
||||
h=restconf_device["conn_args"]["hostname"]
|
||||
),
|
||||
method="GET",
|
||||
decode_type="json",
|
||||
decode=True,
|
||||
verify_ssl=restconf_device["conn_args"]["verify"],
|
||||
username=restconf_device["conn_args"]["username"],
|
||||
password=restconf_device["conn_args"]["password"],
|
||||
header_list=[
|
||||
"Accept: application/yang-data+json",
|
||||
"Content-Type: application/yang-data+json",
|
||||
],
|
||||
)
|
||||
log.debug("restconf_response: {r}".format(r=response))
|
||||
if 'ietf-restconf:yang-library-version' in str(response):
|
||||
if "ietf-restconf:yang-library-version" in str(response):
|
||||
return True, response
|
||||
else:
|
||||
return False, response
|
||||
|
@ -173,48 +174,49 @@ def connection_test():
|
|||
|
||||
def ping():
|
||||
log.debug("restconf proxy ping() called...")
|
||||
'''
|
||||
Connection open successfully?
|
||||
'''
|
||||
# Connection open successfully?
|
||||
return connection_test()[0]
|
||||
|
||||
|
||||
def initialized():
|
||||
'''
|
||||
"""
|
||||
Connection finished initializing?
|
||||
'''
|
||||
return restconf_device.get('initialized', False)
|
||||
"""
|
||||
return restconf_device.get("initialized", False)
|
||||
|
||||
|
||||
def shutdown(opts):
|
||||
'''
|
||||
"""
|
||||
Closes connection with the device.
|
||||
'''
|
||||
log.debug('Shutting down the restconf Proxy Minion %s', opts['id'])
|
||||
"""
|
||||
log.debug("Shutting down the restconf Proxy Minion %s", opts["id"])
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# callable functions
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def request(uri, method='GET', dict_payload=None):
|
||||
def request(uri, method="GET", dict_payload=None):
|
||||
if dict_payload is None:
|
||||
data = ''
|
||||
data = ""
|
||||
elif isinstance(dict_payload, str):
|
||||
data = dict_payload
|
||||
else:
|
||||
data = json.dumps(dict_payload)
|
||||
response = salt.utils.http.query(
|
||||
"https://{h}/{u}".format(h=restconf_device['conn_args']['hostname'], u=uri),
|
||||
method=method,
|
||||
data=data,
|
||||
decode=True,
|
||||
status=True,
|
||||
verify_ssl=restconf_device['conn_args']['verify'],
|
||||
username=restconf_device['conn_args']['username'],
|
||||
password=restconf_device['conn_args']['password'],
|
||||
header_list=['Accept: application/yang-data+json', 'Content-Type: application/yang-data+json']
|
||||
|
||||
)
|
||||
"https://{h}/{u}".format(h=restconf_device["conn_args"]["hostname"], u=uri),
|
||||
method=method,
|
||||
data=data,
|
||||
decode=True,
|
||||
status=True,
|
||||
verify_ssl=restconf_device["conn_args"]["verify"],
|
||||
username=restconf_device["conn_args"]["username"],
|
||||
password=restconf_device["conn_args"]["password"],
|
||||
header_list=[
|
||||
"Accept: application/yang-data+json",
|
||||
"Content-Type: application/yang-data+json",
|
||||
],
|
||||
)
|
||||
log.debug("restconf_request_response: {r}".format(r=response))
|
||||
return response
|
||||
|
|
|
@ -7,15 +7,13 @@ State module for restconf Proxy minions
|
|||
|
||||
"""
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.compat # noqa: F401
|
||||
import json # noqa: F401
|
||||
from salt.utils.odict import OrderedDict # noqa: F401
|
||||
|
||||
from deepdiff import DeepDiff
|
||||
|
||||
# from salt.utils.odict import OrderedDict # noqa: F401
|
||||
|
||||
|
||||
def __virtual__():
|
||||
if "restconf.set_data" in __salt__: # noqa: F821
|
||||
|
@ -23,7 +21,7 @@ def __virtual__():
|
|||
return (False, "restconf module could not be loaded")
|
||||
|
||||
|
||||
def config_manage(name, uri, method, config, init_uri=None, init_method='PATCH'):
|
||||
def config_manage(name, uri, method, config, init_uri=None, init_method="PATCH"):
|
||||
"""
|
||||
Ensure a specific value exists at a given path
|
||||
:param name: The name for this rule
|
||||
|
@ -54,34 +52,36 @@ def config_manage(name, uri, method, config, init_uri=None, init_method='PATCH')
|
|||
# TODO: add template function so that config var does not need to be passed
|
||||
ret = {"name": name, "result": False, "changes": {}, "comment": ""}
|
||||
found_working_uri = False
|
||||
uri_used = ''
|
||||
uri_used = ""
|
||||
existing_raw = __salt__["restconf.get_data"](uri) # noqa: F821
|
||||
request_uri = ''
|
||||
request_method = ''
|
||||
request_uri = ""
|
||||
request_method = ""
|
||||
# TODO: this could probaby be a loop
|
||||
if existing_raw['status'] in [200]:
|
||||
existing = existing_raw['dict']
|
||||
if existing_raw["status"] in [200]:
|
||||
existing = existing_raw["dict"]
|
||||
found_working_uri = True
|
||||
uri_used = 'Primary'
|
||||
uri_used = "Primary"
|
||||
request_uri = uri
|
||||
request_method = method
|
||||
|
||||
if not found_working_uri:
|
||||
existing_raw_init = __salt__["restconf.get_data"](init_uri) # noqa: F821
|
||||
if existing_raw_init['status'] in [200]:
|
||||
existing = existing_raw_init['dict']
|
||||
if existing_raw_init["status"] in [200]:
|
||||
existing = existing_raw_init["dict"]
|
||||
found_working_uri = True
|
||||
uri_used = 'init'
|
||||
uri_used = "init"
|
||||
request_uri = init_uri
|
||||
request_method = init_method
|
||||
|
||||
if not found_working_uri:
|
||||
ret["result"] = False
|
||||
ret["comment"] = 'restconf could not find a working URI to get initial config'
|
||||
ret["comment"] = "restconf could not find a working URI to get initial config"
|
||||
return ret
|
||||
# TODO: END
|
||||
|
||||
dict_config = json.loads(json.dumps(config)) # convert from orderedDict to Dict (which is now ordered by default in python3.8)
|
||||
dict_config = json.loads(
|
||||
json.dumps(config)
|
||||
) # convert from orderedDict to Dict (which is now ordered by default in python3.8)
|
||||
|
||||
if existing == dict_config:
|
||||
ret["result"] = True
|
||||
|
@ -96,33 +96,39 @@ def config_manage(name, uri, method, config, init_uri=None, init_method='PATCH')
|
|||
ret["changes"]["changed"] = diff.changed()
|
||||
|
||||
else:
|
||||
resp = __salt__["restconf.set_data"](request_uri, request_method, dict_config) # noqa: F821
|
||||
resp = __salt__["restconf.set_data"](
|
||||
request_uri, request_method, dict_config
|
||||
) # noqa: F821
|
||||
# Success
|
||||
if resp['status'] in [201, 200, 204]:
|
||||
if resp["status"] in [201, 200, 204]:
|
||||
ret["result"] = True
|
||||
ret["comment"] = "Successfully added config"
|
||||
diff = _restDiff(existing, dict_config)
|
||||
ret["changes"]["new"] = diff.added()
|
||||
ret["changes"]["removed"] = diff.removed()
|
||||
ret["changes"]["changed"] = diff.changed()
|
||||
if method == 'PATCH':
|
||||
if method == "PATCH":
|
||||
ret["changes"]["removed"] = None
|
||||
# full failure
|
||||
else:
|
||||
ret["result"] = False
|
||||
if 'dict' in resp:
|
||||
why = resp['dict']
|
||||
elif 'body' in resp:
|
||||
why = resp['body']
|
||||
if "dict" in resp:
|
||||
why = resp["dict"]
|
||||
elif "body" in resp:
|
||||
why = resp["body"]
|
||||
else:
|
||||
why = None
|
||||
ret["comment"] = "failed to add / modify config. API Statuscode: {s}, API Response: {w}, URI:{u}".format(w=why, s=resp['status'], u=uri_used)
|
||||
ret[
|
||||
"comment"
|
||||
] = "failed to add / modify config. API Statuscode: {s}, API Response: {w}, URI:{u}".format(
|
||||
w=why, s=resp["status"], u=uri_used
|
||||
)
|
||||
print("post_content: {b}".format(b=json.dumps(dict_config)))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class _restDiff(object):
|
||||
class _restDiff:
|
||||
"""
|
||||
Calculate the difference between two dictionaries as:
|
||||
(1) items added
|
||||
|
@ -142,18 +148,18 @@ class _restDiff(object):
|
|||
def added(self):
|
||||
# TODO: Potential for new adds to get missed here.
|
||||
# need to dig into deepdiff more
|
||||
if 'dictionary_item_added' in self.diff.keys():
|
||||
return str(self.diff['dictionary_item_added'])
|
||||
if "dictionary_item_added" in self.diff.keys():
|
||||
return str(self.diff["dictionary_item_added"])
|
||||
return None
|
||||
|
||||
def removed(self):
|
||||
if 'dictionary_item_removed' in self.diff.keys():
|
||||
return str(self.diff['dictionary_item_removed'])
|
||||
if "dictionary_item_removed" in self.diff.keys():
|
||||
return str(self.diff["dictionary_item_removed"])
|
||||
return None
|
||||
|
||||
def changed(self):
|
||||
if 'values_changed' in self.diff.keys():
|
||||
return str(self.diff['values_changed'])
|
||||
if "values_changed" in self.diff.keys():
|
||||
return str(self.diff["values_changed"])
|
||||
return None
|
||||
|
||||
def unchanged(self):
|
||||
|
|
Loading…
Add table
Reference in a new issue