doc update

This commit is contained in:
Jamie Murphy 2020-11-18 16:16:48 +00:00 committed by Megan Wilhite
parent 8cead792f0
commit 7f81bca162
6 changed files with 114 additions and 107 deletions

View file

@ -400,6 +400,7 @@ execution modules
rest_sample_utils
rest_service
restartcheck
restconf
ret
rh_ip
rh_service

View file

@ -31,5 +31,6 @@ proxy modules
panos
philips_hue
rest_sample
restconf
ssh_sample
vcenter

View file

@ -277,6 +277,7 @@ state modules
rdp
redismod
reg
restconf
rsync
rvm
salt_proxy

View file

@ -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

View file

@ -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

View file

@ -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):