Compare commits

...

4 Commits

Author SHA1 Message Date
ddc6c2bb4d influx modules: fixes, permission match, py2, args 2021-05-25 19:13:57 +02:00
f9e29a4e30 influx bucket module 2021-05-25 15:00:47 +02:00
c26e962898 token module improved 2021-05-25 11:55:30 +02:00
38c117d6fa influxdb2 plugins 2021-05-21 20:28:50 +02:00
3 changed files with 540 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python
import requests
from ansible.module_utils.basic import AnsibleModule
DOCUMENTATION = r'''
---
module: influx2_bucket
short_description: create bucket in influxdb2
description: create bucket in influxdb2
notes:
- just works with influxdb version 2
- does not remove buckets
- no way to configure data retention
options:
base:
description: URL for path, e.g. `https://localhost:8086`
type: str
required: True
org:
description: influxdb2 organisation
type: str
required: True
auth_token:
description: influxdb2 authentication token
type: str
required: True
name:
description: name of the bucket
type: str
required: True
force:
description: force creation even if bucket already exists
(adds a new one)
type: bool
required: False
default: False
author:
- Thorsten M. (@thoto)
'''
EXAMPLES = r'''
- name: "fetch auth token"
raw: influx auth list --user my-user --hide-headers | cut -f 3
register: influx_token_fetch
delegate_to: ed-influxdb-2
- name: "create bucket"
influx_bucket:
base: "http://localhost:8086"
org: "my-org"
token: "{{influx_token_fetch.stdout_lines[0]}}"
name: "bucky"
'''
def get_org_id(base, org_name, h):
ro = requests.get("{base}/api/v2/orgs".format(base=base), headers=h,
json={"org": org_name})
ro.raise_for_status()
org_id = [o["id"] for o in ro.json()["orgs"] if o["name"] == org_name]
return org_id[0]
class Bucket:
def __init__(self, base, h, org, description, name):
self.base = base
self.h = h
self.org_id = get_org_id(base, org, h)
self.description = description
self.name = name
self.result = None
self.f = None
def check(self):
ra = requests.get("{base}/api/v2/buckets".format(
base=self.base),
params={"orgID": self.org_id, "name": self.name},
headers=self.h)
if ra.status_code == 404:
# orgID + name -> 404 on empty set. Just name -> 200 but buckets=[]
x = []
else:
ra.raise_for_status()
x = [i for i in ra.json()["buckets"]
if self.name == i["name"] and i["orgID"] == self.org_id]
assert(len(x) == 1 or len(x) == 0)
update = None
if len(x) == 0: # create
self.result = None
self.f = lambda: self._create({
"orgID": self.org_id,
"description": self.description if self.description else None,
"name": self.name,
"retentionRules": []
})
else:
self.result = x[0]
if self.description == x[0].get("description", ""):
return False # everything matches -> no change needed
else:
self.result = x[0]
update = {"id": x[0]["id"],
"description": self.description}
self.f = lambda: self._update(**update)
return True
def run(self):
if not self.f:
self.check()
self.f()
def _update(self, id, description):
ra = requests.patch(
"{base}/api/v2/buckets/{id}".format(
base=self.base, id=id),
headers=self.h, json={"description": description})
ra.raise_for_status()
return ra
def _create(self, data):
ra = requests.post("{base}/api/v2/buckets".format(base=self.base),
headers=self.h, json=data)
ra.raise_for_status()
self.result = ra.json()
return ra
if __name__ == "__main__":
result = dict(changed=False, message="")
module = AnsibleModule(
argument_spec=dict(
base=dict(type="str", required=True),
org=dict(type="str", required=True),
auth_token=dict(type="str", required=True),
name=dict(type="str", required=True),
description=dict(type="str", default=""),
force=dict(type="bool", default=False),
),
supports_check_mode=True
)
h = {"Authorization": "Token {token}".format(
token=module.params["auth_token"])}
b = Bucket(module.params["base"], h, org=module.params["org"],
description=module.params["description"],
name=module.params["name"])
changed = b.check()
if b.result:
result['bucket_id'] = b.result["id"]
result['changed'] = changed
if module.check_mode:
module.exit_json(**result)
if changed or module.params["force"]:
b.run()
result['bucket_id'] = b.result["id"]
module.exit_json(**result)

View File

@@ -0,0 +1,147 @@
#!/usr/bin/env python
import json
import requests
from ansible.module_utils.basic import AnsibleModule
DOCUMENTATION = r'''
---
module: influx2_dashboard
short_description: create dashboard in influxdb2
description: create dashboard in influxdb2
notes:
- just works with influxdb version 2
- does not create dashboard description
- does not update dashboards
- just creates a dashboard if it does not exist.
options:
base:
description: URL for path, e.g. `https://localhost:8086`
type: str
required: True
org:
description: influxdb2 organisation
type: str
required: True
auth_token:
description: influxdb2 authentication token
type: str
required: True
data:
description: exported dashboard json file
type: json
required: True
force:
description: force creation even if dashboard already exists
(adds a new one)
type: bool
required: False
default: False
author:
- Thorsten M. (@thoto)
'''
EXAMPLES = r'''
- name: "fetch auth token"
raw: influx auth list --user my-user --hide-headers | cut -f 3
register: influx_token_fetch
delegate_to: ed-influxdb-2
- name: "create dashboard"
influx_dashboard:
base: "http://localhost:8086"
org: "my-org"
auth_token: "{{influx_token_fetch.stdout_lines[0]}}"
data: "{{lookup('file', 'influxdb-dashboard-cobald.json')}}"
'''
def parse_dashboard_template(data):
j = json.loads(data)
return {
'name': j["content"]["data"]["attributes"]["name"],
'cells': {i["relationships"]["view"]["data"]["id"]: i["attributes"]
for i in j["content"]["included"] if i["type"] == "cell"},
'views': {i["id"]: i["attributes"]
for i in j["content"]["included"] if i["type"] == "view"},
}
def get_auth(base, org_name, token):
h = {"Authorization": "Token {token}".format(token=token)}
rm = requests.get("{base}/api/v2/me".format(base=base), headers=h)
rm.raise_for_status()
# me_name = rm.json()["name"]
me = rm.json()["id"]
ro = requests.get("{base}/api/v2/orgs".format(base=base), headers=h,
json={"userID": me})
ro.raise_for_status()
org_id = [o["id"] for o in ro.json()["orgs"] if o["name"] == org_name]
return {"org_id": org_id[0], "org_name": org_name, "uid": me, "h": h}
def check(base, auth, dashboard):
h = auth["h"]
rd = requests.get("{base}/api/v2/dashboards".format(base=base), headers=h)
rd.raise_for_status()
x = [i for i in rd.json()["dashboards"]
if i["name"] == dashboard["name"] and i["orgID"] == auth["org_id"]]
return len(x) == 0, x
def create(base, auth, dashboard):
h = auth["h"]
# create dashboard
rd = requests.post("{base}/api/v2/dashboards".format(base=base), headers=h,
json={"orgID": auth["org_id"],
"name": dashboard["name"]})
rd.raise_for_status()
dash_id = rd.json()["id"]
for k, v in dashboard["cells"].items():
# create cells in dashboard
rc = requests.post(
"{base}/api/v2/dashboards/{dash_id}/cells".format(
base=base, dash_id=dash_id),
headers=h, json=v)
rc.raise_for_status()
cell_id = rc.json()["id"]
# create view of cell in dashboard
rv = requests.patch(
"{base}/api/v2/dashboards/{dash_id}/cells/{cell_id}/view".format(
base=base, dash_id=dash_id, cell_id=cell_id),
headers=h, json=dashboard["views"][k])
rv.raise_for_status()
if __name__ == "__main__":
result = dict(changed=False, message="")
module = AnsibleModule(
argument_spec=dict(
base=dict(type="str", required=True),
org=dict(type="str", required=True),
auth_token=dict(type="str", required=True),
data=dict(type="json", required=True),
force=dict(type="bool", default=False),
),
supports_check_mode=True
)
dashboard = parse_dashboard_template(module.params["data"])
auth = get_auth(module.params["base"], module.params["org"],
module.params["auth_token"])
changed, x = check(module.params["base"], auth, dashboard)
result['changed'] = changed
if module.check_mode:
module.exit_json(**result)
if changed or module.params["force"]:
create(module.params["base"], auth, dashboard)
module.exit_json(**result)

View File

@@ -0,0 +1,232 @@
#!/usr/bin/env python
import requests
from ansible.module_utils.basic import AnsibleModule
DOCUMENTATION = r'''
---
module: influx2_token
short_description: generate token via influxdb2 api
description: generate token via influxdb2 api
notes:
- just works with influxdb version 2
- needs token to authenticate against API (use
`influx auth list --user my-user --hide-headers | cut -f 3`
- tokens may not be removed
- permissions can not be updated. a new token is created and the old
one is not removed.
options:
base:
description: URL for path, e.g. `https://localhost:8086`
type: str
required: True
org:
description: influxdb2 organisation
type: str
required: True
auth_token:
description: influxdb2 authentication token
type: str
required: True
key:
description: some key used to identify token. This is put into
the tokens description
type: str
required: True
description:
description: textual description for token. key gets appended
type: str
required: False
permissions:
description: list of permissions, each dict(action, resource)
type: list
required: True
force:
description: force creation even if dashboard already exists
(adds a new one)
type: bool
required: False
default: False
author:
- Thorsten M. (@thoto)
'''
EXAMPLES = r'''
- name: "fetch auth token"
raw: influx auth list --user my-user --hide-headers | cut -f 3
register: influx_token_fetch
delegate_to: ed-influxdb-2
- name: "create dashboard"
influx_token:
base: "http://localhost:8086"
org: "my-org"
auth_token: "{{influx_token_fetch.stdout_lines[0]}}"
key: "foo123"
description: "token for foo key"
permissions:
- action: "write"
resource:
type: "buckets"
register: ed-influx-token
- debug: msg="Token: {{ed-influx-token.token}}"
'''
def get_org_id(base, org_name, h):
ro = requests.get("{base}/api/v2/orgs".format(base=base), headers=h,
json={"org": org_name})
ro.raise_for_status()
org_id = [o["id"] for o in ro.json()["orgs"] if o["name"] == org_name]
return org_id[0]
def marker(key):
return "__ANSIBLE_TOKEN_{key}__".format(key=key)
def _filter_perms(perms):
for r in perms:
r["resource"] = {k: v for k, v in r["resource"].items() if v}
return perms
class Token:
def __init__(self, base, h, data):
self.base = base
self.h = h
self.marker = marker(data["key"])
self.org_id = data["org_id"]
self.perms = _filter_perms(data["perms"])
self.description = data["description"]+" "+self.marker
self.result_token = None
self.f = None
def check(self):
ra = requests.get("{base}/api/v2/authorizations".format(
base=self.base),
params={"orgID": self.org_id},
headers=self.h)
ra.raise_for_status()
update = None
for i in ra.json()["authorizations"]:
if self.marker not in i["description"] \
or i["orgID"] != self.org_id:
continue
if self._match_perms(self.perms, i["permissions"]):
self.result_token = i
if self.description == i["description"]:
return False # everything matches -> no change needed
else:
update = {"auth_id": i["id"],
"description": self.description}
# TODO: may remove token because permissions do not match?
if update:
self.f = lambda: self._update(**update)
else:
self.result_token = None
self.f = lambda: self._create({
"orgID": self.org_id,
"description": self.description,
"permissions": self.perms
})
return True
def run(self):
if not self.f:
self.check()
self.f()
def _match_perms(self, pa, pb):
def g(match, lst):
for idx, i in enumerate(lst):
if i['action'] != match['action']:
continue
for k, v in match['resource'].items():
if k not in i['resource'] or i['resource'][k] != v:
continue
else: # first best match
return idx
else:
raise ValueError
b = [b.copy() for b in pb]
for i in pa:
try:
b.pop(g(i, b))
except ValueError:
return False # permission i not present in b
if b: # not empty
return False # some permissions in b not in a
return True
def _update(self, auth_id, description):
ra = requests.patch(
"{base}/api/v2/authorizations/{auth_id}".format(
base=self.base, auth_id=auth_id),
headers=self.h, json={"description": description})
ra.raise_for_status()
return ra
def _create(self, data):
ra = requests.post("{base}/api/v2/authorizations".format(
base=self.base),
headers=self.h, json=data)
ra.raise_for_status()
self.result_token = ra.json()
return ra
if __name__ == "__main__":
result = dict(changed=False, message="")
module = AnsibleModule(
argument_spec=dict(
base=dict(type="str", required=True),
org=dict(type="str", required=True),
auth_token=dict(type="str", required=True),
key=dict(type="str", required=True),
description=dict(type="str", default=""),
permissions=dict(type="list", elements='dict', options=dict(
action=dict(type='str', choices=['read', 'write'],
required=True),
resource=dict(type='dict', options=dict(
id=dict(type='str'),
name=dict(type='str'),
org=dict(type='str'),
orgID=dict(type='str'),
type=dict(type='str', required=True, choices=[
"authorizations", "buckets", "dashboards", "orgs",
"sources", "tasks", "telegrafs", "users", "variables",
"scrapers", "secrets", "labels", "views", "documents",
"notificationRules", "notificationEndpoints", "checks",
"dbrp", "flows", "annotations", "functions"]),
), required=True),
), required=True),
force=dict(type="bool", default=False),
),
supports_check_mode=True
)
h = {"Authorization": "Token {token}".format(
token=module.params["auth_token"])}
t = Token(module.params["base"], h, {
"org_id": get_org_id(module.params["base"], module.params["org"], h),
"key": module.params["key"],
"perms": module.params["permissions"],
"description": module.params["description"]})
changed = t.check()
if t.result_token:
result['token'] = t.result_token["token"]
result['changed'] = changed
if module.check_mode:
module.exit_json(**result)
if changed or module.params["force"]:
t.run()
result['token'] = t.result_token["token"]
module.exit_json(**result)