blob: c59efe01f076278dc53dc09dd400d62c878988e5 [file] [log] [blame]
David K. Bainbridge6e23ac82016-12-07 12:55:41 -08001#!/usr/bin/python
2
Jonathan Hart93956f52017-08-22 13:12:42 -07003# Copyright 2017-present Open Networking Foundation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
David K. Bainbridge6e23ac82016-12-07 12:55:41 -080017DOCUMENTATION = '''
18---
19module: maas_item
20short_description: Manage MAAS Clusters Interfaces
21options:
22 maas:
23 description:
24 - URL of MAAS server
25 default: http://localhost/MAAS/api/1.0/
26 key:
27 description:
28 - MAAS API key
29 required: yes
30 name:
31 description:
32 - name of the item
33 required: yes
34 state:
35 description:
36 - possible states for this item
37 choices: ['present', 'absent', 'query']
38 default: present
39
40requirements: [ipaddress, requests_oauthlib, maasclient]
41author: David Bainbridge
42'''
43
44EXAMPLES = '''
45examples:
46 maas_item:
47 maas: http://my.maas.server.com/MAAS/api/1.0/
48 key: 'xBvr9dx5k7S52myufC:fqBXV7hJgXegNZDw9c:K8hsmL47XjAppfQy2pDVW7G49p6PELgp'
49 name: MyItem
50 state: present
51
52 maas_item:
53 maas: http://my.maas.server.com/MAAS/api/1.0/
54 key: 'xBvr9dx5k7S52myufC:fqBXV7hJgXegNZDw9c:K8hsmL47XjAppfQy2pDVW7G49p6PELgp'
55 name: MyDeadItem
56 state: absent
57'''
58
59import sys
60import json
61import ipaddress
62import requests
63from maasclient.auth import MaasAuth
64from maasclient import MaasClient
65
66# For some reason the maasclient doesn't provide a put method. So
67# we will add it here
68def put(client, url, params=None):
69 return requests.put(url=client.auth.api_url + url,
70 auth=client._oauth(), data=params)
71
72# Attempt to interpret the given value as a JSON object, if that fails
73# just return it as a string
74def string_or_object(val):
75 try:
76 return json.loads(val)
77 except:
78 return val
79
80# Return a copy of the given dictionary with any `null` valued entries
81# removed
82def remove_null(d_in):
83 d = d_in.copy()
84 to_remove = []
85 for k in d.keys():
86 if d[k] == None:
87 to_remove.append(k)
88 for k in to_remove:
89 del d[k]
90 return d
91
92# Deterine if two dictionaries are different
93def different(have, want):
94 have_keys = have.keys()
95 for key in want.keys():
96 if (key in have_keys and want[key] != have[key]) or key not in have_keys:
97 return True
98 return False
99
100# Get an item from MAAS using its name, if not found return None
101def get_item(maas, name):
102 res = maas.get('/items/%s/' % name)
103 if res.ok:
104 return json.loads(res.text)
105 return None
106
107# Create an item based on the value given
108def create_item(maas, item):
109 merged = item.copy()
110 # merged['op'] = 'new'
111 res = maas.post('/items/', merged)
112 if res.ok:
113 return { 'error': False, 'status': get_item(maas, merged['name']) }
114 return { 'error': True, 'status': string_or_object(res.text) }
115
116# Delete an item based on the name
117def delete_item(maas, name):
118 res = maas.delete('/items/%s/' % name)
119 if res.ok:
120 return { 'error': False }
121 return { 'error': True, 'status': string_or_object(res.text) }
122
123def update_item(maas, have, want):
124 merged = have.copy()
125 merged.update(want)
126 res = put(maas, '/items/%s/' % merged['name'], merged)
127 if res.ok:
128 return { 'error': False, 'status': get_item(maas, merged['name']) }
129 return { 'error': True, 'status': string_or_object(res.text) }
130
131def main():
132 module = AnsibleModule(
133 argument_spec = dict(
134 maas=dict(default='http://localhost/MAAS/api/1.0/'),
135 key=dict(required=True),
136 name=dict(required=True),
137 state=dict(default='present', choices=['present', 'absent', 'query'])
138 ),
139 supports_check_mode = False
140 )
141
142 maas = module.params['maas']
143 key = module.params['key']
144 state = module.params['state']
145
146 # Construct a sparsely populate desired state
147 desired = remove_null({
148 'name': module.params['name'],
149 })
150
151 # Authenticate into MAAS
152 auth = MaasAuth(maas, key)
153 maas = MaasClient(auth)
154
155 # Attempt to get the item from MAAS
156 item = get_item(maas, desired['name'])
157
158 # Actions if the item does not currently exist
159 if not item:
160 if state == 'query':
161 # If this is a query, returne it is not found
162 module.exit_json(changed=False, found=False)
163 elif state == 'present':
164 # If this should be present, then attempt to create it
165 res = create_item(maas, desired)
166 if res['error']:
167 module.fail_json(msg=res['status'])
168 else:
169 module.exit_json(changed=True, item=res['status'])
170 else:
171 # If this should be absent, then we are done and in the desired state
172 module.exit_json(changed=False)
173
174 # Done with items does not exists actions
175 return
176
177 # Actions if the item does exist
178 if state == 'query':
179 # If this is a query, return the item
180 module.exit_json(changed=False, found=True, item=item)
181 elif state == 'present':
182 # If we want this to exists check to see if this is different and
183 # needs updated
184 if different(item, desired):
185 res = update_item(maas, item, desired)
186 if res['error']:
187 module.fail_json(msg=res['status'])
188 else:
189 module.exit_json(changed=True, item=res['status'])
190 else:
191 # No differences, to nothing to change
192 module.exit_json(changed=False, item=item)
193 else:
194 # If we don't want this item, then delete it
195 res = delete_item(maas, item['name'])
196 if res['error']:
197 module.fail_json(msg=res['status'])
198 else:
199 module.exit_json(changed=True, item=item)
200
201# this is magic, see lib/ansible/module_common.py
202#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
203if __name__ == '__main__':
204 main()