Skip to content

Commit bffc515

Browse files
caladdbsanders
authored andcommitted
FEATURE: Ansible module to manage Stacki attributes
An Ansible module for managing Stacki attributes. The module takes several optional parameters: `scope` for specifying the scope of the attribute. Valid choices are: `global` (default), `appliance`, `os`, `environment`, and `host`. `name` for requesting the data for a specific item in the scope. Required if the scope is not `global`. `attr` to specify the attribute to manage. `shadow` to control if the attribute type is shadow. Defaults to False. Example playbook: ``` --- - hosts: localhost tasks: - name: Add a global attribute stacki_attribute: attr: global_attr value: test register: result - name: Add a global attribute output debug: var: result - name: Update the global attribute stacki_attribute: attr: global_attr value: foo register: result - name: Update the global attribute output debug: var: result - name: Add a shadow host attribute stacki_attribute: name: backend-0-0 scope: host attr: my_secret value: foo shadow: yes register: result - name: Add a shadow host attribute output debug: var: result - name: Remove a shadow host attribute stacki_attribute: name: backend-0-0 scope: host attr: my_secret shadow: yes state: absent register: result - name: Remove a shadow host attribute output debug: var: result ``` Output of the debug commands, showing the structure of the data returned: ``` TASK [Add a global attribute output] ************************************************************************************************************ ok: [localhost] => { "result": { "changed": true, "failed": false } } TASK [Update the global attribute output] ******************************************************************************************************* ok: [localhost] => { "result": { "changed": true, "failed": false } } TASK [Add a shadow host attribute output] ******************************************************************************************************* ok: [localhost] => { "result": { "changed": true, "failed": false } } TASK [Remove a shadow host attribute output] **************************************************************************************************** ok: [localhost] => { "result": { "changed": true, "failed": false } } ```
1 parent d519324 commit bffc515

File tree

2 files changed

+660
-0
lines changed

2 files changed

+660
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# @copyright@
2+
# Copyright (c) 2006 - 2020 Teradata
3+
# All rights reserved. Stacki(r) v5.x stacki.com
4+
# https://github.com/Teradata/stacki/blob/master/LICENSE.txt
5+
# @copyright@
6+
7+
DOCUMENTATION = """
8+
module: stacki_attribute
9+
short_description: Manage Stacki attributes
10+
description:
11+
- Add, modify, and remove Stacki attributes
12+
13+
options:
14+
name:
15+
description:
16+
- The name of the scoped item to manage
17+
type: str
18+
required: false
19+
20+
scope:
21+
description:
22+
- The scope of the attribute to manage
23+
type: str
24+
required: false
25+
choices: ['global', 'appliance', 'os', 'environment', 'host']
26+
default: global
27+
28+
attr:
29+
description:
30+
- The name of the attribute to manage, in the given scope
31+
required: true
32+
type: str
33+
34+
value:
35+
description:
36+
- The value to assign to the attribute
37+
required: When state is present
38+
type: str
39+
40+
shadow:
41+
description:
42+
- Is the attribute in the shadow database (only readable by root and apache)
43+
type: bool
44+
required: false
45+
default: no
46+
47+
state:
48+
description:
49+
- If present, then an attribute will be added (if needed) and value set to match
50+
- If absent, then the attribute will be removed
51+
type: str
52+
choices: [ absent, present ]
53+
default: present
54+
"""
55+
56+
EXAMPLES = """
57+
- name: Add a global attribute
58+
stacki_attribute:
59+
attr: global_attr
60+
value: test
61+
62+
- name: Update the global attribute
63+
stacki_attribute:
64+
attr: global_attr
65+
value: foo
66+
67+
- name: Add a shadow host attribute
68+
stacki_attribute:
69+
name: backend-0-0
70+
scope: host
71+
attr: my_secret
72+
value: foo
73+
shadow: yes
74+
75+
- name: Remove a shadow host attribute
76+
stacki_attribute:
77+
name: backend-0-0
78+
scope: host
79+
attr: my_secret
80+
shadow: yes
81+
state: absent
82+
"""
83+
84+
RETURN = """ # """
85+
86+
from ansible.module_utils.basic import AnsibleModule
87+
from ansible.module_utils.stacki import run_stack_command, StackCommandError
88+
89+
90+
def main():
91+
# Define the arguments for this module
92+
argument_spec = dict(
93+
name=dict(type="str", required=False, default=None),
94+
scope=dict(
95+
type="str", required=False, default="global",
96+
choices=["global", "appliance", "os", "environment", "host"]
97+
),
98+
attr=dict(type="str", required=True),
99+
value=dict(type="str", required=False, default=None),
100+
shadow=dict(type="bool", required=False, default=False),
101+
state=dict(type="str", default="present", choices=["absent", "present"])
102+
)
103+
104+
# Create our module object
105+
module = AnsibleModule(
106+
argument_spec=argument_spec,
107+
supports_check_mode=True
108+
)
109+
110+
# Initialize a blank result
111+
result = {
112+
"changed": False
113+
}
114+
115+
# Make sure we have a name if scope isn't global
116+
if not module.params["name"] and module.params["scope"] != "global":
117+
module.fail_json(msg="error - name is required for non-global scope", **result)
118+
119+
# Make sure value is provided if state isn't absent
120+
if not module.params["value"] and module.params["state"] != "absent":
121+
module.fail_json(msg="error - value is required", **result)
122+
123+
# Bail if the user is just checking syntax of their playbook
124+
if module.check_mode:
125+
module.exit_json(**result)
126+
127+
# Fetch our attribute info from Stacki
128+
args = []
129+
if module.params["name"]:
130+
args.append(module.params["name"])
131+
132+
for field in ("scope", "attr"):
133+
args.append(f"{field}={module.params[field]}")
134+
135+
attributes = run_stack_command("list.attr", args)
136+
137+
if len(attributes) > 1:
138+
# No more than one attribute should match
139+
module.fail_json(msg="error - more than one attribute matches attr", **result)
140+
141+
# Make sure the shadow state matches a returned attr
142+
attribute = None
143+
if len(attributes):
144+
# The returned attribute type has to match the shadow flag
145+
if module.params["shadow"] and attributes[0]["type"] == "shadow":
146+
attribute = attributes[0]
147+
elif not module.params["shadow"] and attributes[0]["type"] == "var":
148+
attribute = attributes[0]
149+
150+
try:
151+
# Are we adding or removing?
152+
if module.params["state"] == "present":
153+
# Do we need to add or modify the attribute?
154+
if attribute is None or attribute["value"] != module.params["value"]:
155+
# Append the args missing to the existing list
156+
for field in ("value", "shadow"):
157+
args.append(f"{field}={module.params[field]}")
158+
159+
# Create or update the attribute
160+
run_stack_command("set.attr", args)
161+
result["changed"] = True
162+
else:
163+
# Do we have an attribute to remove?
164+
if attribute:
165+
# Append the shadow arg to our existing list
166+
args.append(f'shadow={module.params["shadow"]}')
167+
168+
# Remove the attribute
169+
run_stack_command("remove.attr", args)
170+
result["changed"] = True
171+
172+
except StackCommandError as e:
173+
# Fetching the data failed
174+
module.fail_json(msg=e.message, **result)
175+
176+
# Return our data
177+
module.exit_json(**result)
178+
179+
180+
if __name__ == "__main__":
181+
main()

0 commit comments

Comments
 (0)