aruba: Early httpapi plugin
This commit is contained in:
parent
1deba6ebf8
commit
f4096cf5af
4 changed files with 152 additions and 154 deletions
|
@ -7,6 +7,7 @@ retry_files_enabled = False
|
||||||
inventory = ./hosts
|
inventory = ./hosts
|
||||||
stdout_callback = debug
|
stdout_callback = debug
|
||||||
library = ./library
|
library = ./library
|
||||||
|
httpapi_plugins = ./httpapi
|
||||||
filter_plugins = ./filter_plugins
|
filter_plugins = ./filter_plugins
|
||||||
ansible_managed = Ansible managed
|
ansible_managed = Ansible managed
|
||||||
nocows = 1
|
nocows = 1
|
||||||
|
|
49
httpapi/aruba.py
Normal file
49
httpapi/aruba.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from ansible.module_utils.six.moves.urllib.error import HTTPError
|
||||||
|
from ansible_collections.ansible.netcommon.plugins.plugin_utils.httpapi_base import (
|
||||||
|
HttpApiBase,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HttpApi(HttpApiBase):
|
||||||
|
def login(self, username, password):
|
||||||
|
"""
|
||||||
|
Log in to the rest api.
|
||||||
|
Return True if the connection has succeeded and False otherwise.
|
||||||
|
"""
|
||||||
|
data = {"userName": username, "password": password}
|
||||||
|
response = self.send_request(json.dumps(data), path="/login-sessions")
|
||||||
|
|
||||||
|
if response.status_code != 201:
|
||||||
|
return AnsibleAuthentificationFailure(message="Plop!")
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
if not "cookie" in data:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.headers["cookie"] = data["cookie"]
|
||||||
|
return True
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
"""
|
||||||
|
Log out of the rest api.
|
||||||
|
Return True if connection has succeeded and False otherwise
|
||||||
|
"""
|
||||||
|
response = self.delete("/login-sessions")
|
||||||
|
if response.status_code != 204:
|
||||||
|
return False
|
||||||
|
self.headers.pop("cookie")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def send_request(self, data, path, method="POST"):
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
uri = self.get_option("uri_root_path") + "/" + path
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
content = json.dumps(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response, content = self.connection.send(uri, content, method=method, headers=headers)
|
||||||
|
except HTTPError as exc:
|
||||||
|
return exc.code, exc.read()
|
||||||
|
|
||||||
|
return response.read()
|
|
@ -73,107 +73,58 @@ EXAMPLES = """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.connection import Connection
|
||||||
|
|
||||||
import json
|
# class SwitchApi:
|
||||||
import os
|
# def __init__(self, port, host, use_proxy, api="v1"):
|
||||||
import requests
|
# self.headers = {"Content-Type": "application/json"}
|
||||||
import time
|
# self.url_base = f"http://{host}:{port}/rest/{api}"
|
||||||
|
|
||||||
class SwitchApi:
|
# self.proxies = None
|
||||||
def __init__(self, port, host, use_proxy, api="v1"):
|
# if use_proxy:
|
||||||
self.headers = {'Content-Type': 'application/json'}
|
# http_proxy = os.getenv("HTTP_PROXY")
|
||||||
self.url_base = f"http://{host}:{port}/rest/{api}"
|
# all_proxy = os.getenv("ALL_PROXY")
|
||||||
|
|
||||||
self.proxies = None
|
# if http_proxy != "":
|
||||||
if use_proxy:
|
# self.proxies = {"http": http_proxy}
|
||||||
http_proxy = os.getenv("HTTP_PROXY")
|
# elif all_proxy != "":
|
||||||
all_proxy = os.getenv("ALL_PROXY")
|
# self.proxies = {"http": all_proxy}
|
||||||
|
|
||||||
if http_proxy != "":
|
# def post(self, url, data=None):
|
||||||
self.proxies = {'http': http_proxy}
|
# kwargs = {"headers": self.headers}
|
||||||
elif all_proxy != "":
|
# if data is not None:
|
||||||
self.proxies = {'http': all_proxy}
|
# kwargs["data"] = data
|
||||||
|
# if self.proxies is not None:
|
||||||
|
# kwargs["proxies"] = self.proxies
|
||||||
|
|
||||||
def login(self, username, password):
|
# return requests.post(self.url_base + url, **kwargs)
|
||||||
"""
|
|
||||||
Log in to the rest api.
|
|
||||||
Return True if the connection has succeeded and False otherwise.
|
|
||||||
"""
|
|
||||||
data = {"userName": username, "password": password}
|
|
||||||
max_retries = 3
|
|
||||||
for i in range(max_retries):
|
|
||||||
try:
|
|
||||||
response = self.post("/login-sessions", data = json.dumps(data),)
|
|
||||||
break
|
|
||||||
except requests.exceptions.ConnectionError as err:
|
|
||||||
if i == max_retries-1:
|
|
||||||
raise err
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
if response.status_code != 201:
|
# def get(self, url, data=""):
|
||||||
return False
|
# kwargs = {"headers": self.headers}
|
||||||
|
# if data is not None:
|
||||||
|
# kwargs["data"] = data
|
||||||
|
# if self.proxies is not None:
|
||||||
|
# kwargs["proxies"] = self.proxies
|
||||||
|
|
||||||
data = response.json()
|
# return requests.get(self.url_base + url, **kwargs)
|
||||||
if not 'cookie' in data:
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.headers['cookie'] = data['cookie']
|
# def delete(self, url, data=""):
|
||||||
return True
|
# kwargs = {"headers": self.headers}
|
||||||
|
# if data is not None:
|
||||||
|
# kwargs["data"] = data
|
||||||
|
# if self.proxies is not None:
|
||||||
|
# kwargs["proxies"] = self.proxies
|
||||||
|
|
||||||
def logout(self):
|
# return requests.delete(self.url_base + url, **kwargs)
|
||||||
"""
|
|
||||||
Log out of the rest api.
|
|
||||||
Return True if connection has succeeded and False otherwise
|
|
||||||
"""
|
|
||||||
response = self.delete("/login-sessions")
|
|
||||||
if response.status_code != 204:
|
|
||||||
return False
|
|
||||||
self.headers.pop('cookie')
|
|
||||||
return True
|
|
||||||
|
|
||||||
def post(self, url, data = None):
|
# def put(self, url, data=""):
|
||||||
kwargs = {
|
# kwargs = {"headers": self.headers}
|
||||||
"headers": self.headers
|
# if data is not None:
|
||||||
}
|
# kwargs["data"] = data
|
||||||
if data is not None:
|
# if self.proxies is not None:
|
||||||
kwargs["data"] = data
|
# kwargs["proxies"] = self.proxies
|
||||||
if self.proxies is not None:
|
|
||||||
kwargs["proxies"] = self.proxies
|
|
||||||
|
|
||||||
return requests.post(self.url_base + url, **kwargs)
|
# return requests.put(self.url_base + url, **kwargs)
|
||||||
|
|
||||||
def get(self, url, data = ""):
|
|
||||||
kwargs = {
|
|
||||||
"headers": self.headers
|
|
||||||
}
|
|
||||||
if data is not None:
|
|
||||||
kwargs["data"] = data
|
|
||||||
if self.proxies is not None:
|
|
||||||
kwargs["proxies"] = self.proxies
|
|
||||||
|
|
||||||
return requests.get(self.url_base + url, **kwargs)
|
|
||||||
|
|
||||||
def delete(self, url, data = ""):
|
|
||||||
kwargs = {
|
|
||||||
"headers": self.headers
|
|
||||||
}
|
|
||||||
if data is not None:
|
|
||||||
kwargs["data"] = data
|
|
||||||
if self.proxies is not None:
|
|
||||||
kwargs["proxies"] = self.proxies
|
|
||||||
|
|
||||||
return requests.delete(self.url_base + url, **kwargs)
|
|
||||||
|
|
||||||
def put(self, url, data = ""):
|
|
||||||
kwargs = {
|
|
||||||
"headers": self.headers
|
|
||||||
}
|
|
||||||
if data is not None:
|
|
||||||
kwargs["data"] = data
|
|
||||||
if self.proxies is not None:
|
|
||||||
kwargs["proxies"] = self.proxies
|
|
||||||
|
|
||||||
return requests.put(self.url_base + url, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def required_modification(current_conf, modification):
|
def required_modification(current_conf, modification):
|
||||||
|
@ -184,33 +135,37 @@ def required_modification(current_conf, modification):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def throw_err(msg, url, status, response):
|
||||||
|
raise Exception(
|
||||||
|
msg + ":",
|
||||||
|
"Url: " + url,
|
||||||
|
"Status code: " + status,
|
||||||
|
"Response: " + response,
|
||||||
|
)
|
||||||
|
|
||||||
def configure(module, config, api, current_path="", create_method=None):
|
|
||||||
|
def configure(connection, config, check_mode, current_path="", create_method=None):
|
||||||
path = "/" + str(config["path"])
|
path = "/" + str(config["path"])
|
||||||
url = current_path + path
|
url = current_path + path
|
||||||
check_mode = module.check_mode
|
|
||||||
changed = False
|
changed = False
|
||||||
before = {"path": path}
|
before = {"path": path}
|
||||||
after = {"path": path}
|
after = {"path": path}
|
||||||
|
|
||||||
if not "path" in config:
|
if not "path" in config:
|
||||||
api.logout()
|
|
||||||
raise Exception("A path must be specified.")
|
raise Exception("A path must be specified.")
|
||||||
|
|
||||||
# If removing configuration
|
# If removing configuration
|
||||||
if "delete" in config and config["delete"]:
|
if "delete" in config and config["delete"]:
|
||||||
# Get the configuration
|
# Get the configuration
|
||||||
response = api.get(url)
|
status, response = connection.send_request(None, url, method="GET")
|
||||||
if response.status_code == 404:
|
if status == 404:
|
||||||
before["delete"] = True
|
before["delete"] = True
|
||||||
elif response.status_code in (200, 201, 202, 203, 204):
|
elif status in (200, 201, 202, 203, 204):
|
||||||
before["data"] = response.json()
|
before["data"] = response.json()
|
||||||
else:
|
else:
|
||||||
api.logout()
|
throw_err("Failed to check the old configuration",
|
||||||
raise Exception(
|
f"Url: {url}",
|
||||||
"Failed to check the old configuration:",
|
f"Status code: {status}",
|
||||||
f"Url: {response.url}",
|
|
||||||
f"Status code: {response.status_code}",
|
|
||||||
f"Response: {response.text}",
|
f"Response: {response.text}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -251,20 +206,17 @@ def configure(module, config, api, current_path="", create_method=None):
|
||||||
# If create or edit
|
# If create or edit
|
||||||
elif "data" in config and type(config["data"]) is dict:
|
elif "data" in config and type(config["data"]) is dict:
|
||||||
# Get the configuration
|
# Get the configuration
|
||||||
response = api.get(url)
|
status, response = connection.send_request(None, url, method="GET")
|
||||||
new_data = {}
|
new_data = {}
|
||||||
if response.status_code == 404:
|
if status == 404:
|
||||||
before["delete"] = True
|
before["delete"] = True
|
||||||
elif response.status_code in (200, 201, 202, 203, 204):
|
elif status in (200, 201, 202, 203, 204):
|
||||||
before["data"] = response.json()
|
before["data"] = response.json()
|
||||||
new_data = before["data"].copy()
|
new_data = before["data"].copy()
|
||||||
else:
|
else:
|
||||||
api.logout()
|
throw_err(
|
||||||
raise Exception(
|
"Failed to check the old configuration",
|
||||||
"Failed to check the old configuration:",
|
url, status, response
|
||||||
f"Url: {response.url}",
|
|
||||||
f"Status code: {response.status_code}",
|
|
||||||
f"Response: {response.text}",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# If required, modify
|
# If required, modify
|
||||||
|
@ -317,7 +269,11 @@ def configure(module, config, api, current_path="", create_method=None):
|
||||||
)
|
)
|
||||||
new_data.update(config["data"])
|
new_data.update(config["data"])
|
||||||
after["data"] = new_data
|
after["data"] = new_data
|
||||||
changed = changed or (not "data" in before) or after["data"] != before["data"]
|
changed = (
|
||||||
|
changed
|
||||||
|
or (not "data" in before)
|
||||||
|
or after["data"] != before["data"]
|
||||||
|
)
|
||||||
|
|
||||||
# Configure the subpaths
|
# Configure the subpaths
|
||||||
if "subpath" in config and type(config["subpath"]) is list:
|
if "subpath" in config and type(config["subpath"]) is list:
|
||||||
|
@ -328,66 +284,46 @@ def configure(module, config, api, current_path="", create_method=None):
|
||||||
after["subpath"] = []
|
after["subpath"] = []
|
||||||
for subconf in config["subpath"]:
|
for subconf in config["subpath"]:
|
||||||
response = configure(
|
response = configure(
|
||||||
module, subconf,
|
connection,
|
||||||
|
subconf,
|
||||||
api,
|
api,
|
||||||
current_path=url,
|
current_path=url,
|
||||||
create_method=create_method
|
create_method=create_method,
|
||||||
)
|
)
|
||||||
changed = changed or response["changed"]
|
changed = changed or response["changed"]
|
||||||
before["subpath"].append(response["diff"]["before"])
|
before["subpath"].append(response["diff"]["before"])
|
||||||
after["subpath"].append(response["diff"]["after"])
|
after["subpath"].append(response["diff"]["after"])
|
||||||
|
|
||||||
return {
|
return {"changed": changed, "diff": {"after": after, "before": before}}
|
||||||
"changed": changed,
|
|
||||||
"diff": {"after": after, "before": before}
|
|
||||||
}
|
|
||||||
|
|
||||||
def run_module():
|
def run_module():
|
||||||
module_args = dict(
|
module_args = {
|
||||||
config=dict(type='dict', required=True),
|
"config": {"type": "dict", "required": True},
|
||||||
username=dict(type='str', required=True),
|
"username": {"type": "str", "required": True},
|
||||||
password=dict(type='str', required=True, no_log=True),
|
"password": {"type": "str", "required": True, "no_log": True},
|
||||||
port=dict(type='int', required=True),
|
"port": {"type": "int", "required": True},
|
||||||
host=dict(type='str', required=True),
|
"host": {"type": "str", "required": True},
|
||||||
version=dict(type='str', required=False, default='v1'),
|
"version": {"type": "str", "required": False, "default": "v1"},
|
||||||
use_proxy=dict(type='bool', required=False, default=False),
|
"use_proxy": {"type": "bool", "required": False, "default": False},
|
||||||
)
|
}
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
|
||||||
argument_spec=module_args,
|
|
||||||
supports_check_mode=True
|
connection = Connection(module._socket_path)
|
||||||
)
|
connection.set_option("uri_root_path", module.params["host"])
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"changed": False,
|
"changed": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
# api connection
|
|
||||||
api = SwitchApi(
|
|
||||||
module.params["port"],
|
|
||||||
module.params["host"],
|
|
||||||
module.params["use_proxy"],
|
|
||||||
api = module.params["version"]
|
|
||||||
)
|
|
||||||
login_success = api.login(
|
|
||||||
module.params["username"],
|
|
||||||
module.params["password"],
|
|
||||||
)
|
|
||||||
if not login_success:
|
|
||||||
module.fail_json(msg='login failed', **result)
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = configure(module, module.params["config"], api)
|
response = configure(connection, module.params["config"], module.check_mode)
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
module.fail_json(msg="\n".join(msg.args), **result)
|
module.fail_json(msg="\n".join(msg.args), **result)
|
||||||
return
|
return
|
||||||
api.logout()
|
|
||||||
result.update(response)
|
result.update(response)
|
||||||
|
|
||||||
if module.check_mode:
|
|
||||||
module.exit_json(**result)
|
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,5 +331,5 @@ def main():
|
||||||
run_module()
|
run_module()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -2,13 +2,25 @@
|
||||||
---
|
---
|
||||||
- hosts:
|
- hosts:
|
||||||
- switch
|
- switch
|
||||||
connection: httpapi
|
|
||||||
gather_facts: false
|
|
||||||
environment:
|
environment:
|
||||||
HTTP_PROXY: "socks5://localhost:3000"
|
HTTP_PROXY: "socks5://localhost:3000"
|
||||||
|
|
||||||
|
connection: httpapi
|
||||||
|
gather_facts: false
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
|
ansible_network_os: aruba
|
||||||
|
|
||||||
|
ansible_user: vault_switch.username
|
||||||
|
ansible_httpapi_password: vault_switch.password
|
||||||
|
|
||||||
|
ansible_httpapi_use_ssl: false
|
||||||
|
ansible_httpapi_validate_certs: false
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
use_proxy: true
|
use_proxy: true
|
||||||
|
|
||||||
roles:
|
roles:
|
||||||
- switch-system
|
- switch-system
|
||||||
- switch-vlans
|
- switch-vlans
|
||||||
|
|
Loading…
Reference in a new issue