blob: abe88f5dab100bc2bc312f2878ea4e331fd7d3b2 [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_zone
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 zone
33 required: yes
34 description:
35 description:
36 - description text of zone
37 required: no
38 state:
39 description:
40 - possible states for this zone
41 choices: ['present', 'absent', 'query']
42 default: present
43
44requirements: [ipaddress, requests_oauthlib, maasclient]
45author: David Bainbridge
46'''
47
48EXAMPLES = '''
49examples:
50 maas_zone:
51 maas: http://my.maas.server.com/MAAS/api/1.0/
52 key: 'xBvr9dx5k7S52myufC:fqBXV7hJgXegNZDw9c:K8hsmL47XjAppfQy2pDVW7G49p6PELgp'
53 name: MyZone
54 description: This is my zone
55 state: present
56
57 maas_zone:
58 maas: http://my.maas.server.com/MAAS/api/1.0/
59 key: 'xBvr9dx5k7S52myufC:fqBXV7hJgXegNZDw9c:K8hsmL47XjAppfQy2pDVW7G49p6PELgp'
60 name: MyDeadZone
61 description: This was my zone
62 state: absent
63'''
64
65import sys
66import json
67import ipaddress
68import requests
69from maasclient.auth import MaasAuth
70from maasclient import MaasClient
71
72# For some reason the maasclient doesn't provide a put method. So
73# we will add it here
74def put(client, url, params=None):
75 return requests.put(url=client.auth.api_url + url,
76 auth=client._oauth(), data=params)
77
78# Attempt to interpret the given value as a JSON object, if that fails
79# just return it as a string
80def string_or_object(val):
81 try:
82 return json.loads(val)
83 except:
84 return val
85
86# Return a copy of the given dictionary with any `null` valued entries
87# removed
88def remove_null(d_in):
89 d = d_in.copy()
90 to_remove = []
91 for k in d.keys():
92 if d[k] == None:
93 to_remove.append(k)
94 for k in to_remove:
95 del d[k]
96 return d
97
98# Deterine if two dictionaries are different
99def different(have, want):
100 have_keys = have.keys()
101 for key in want.keys():
102 if (key in have_keys and want[key] != have[key]) or key not in have_keys:
103 return True
104 return False
105
106# Get an zone from MAAS using its name, if not found return None
107def get_zone(maas, name):
108 res = maas.get('/zones/%s/' % name)
109 if res.ok:
110 return json.loads(res.text)
111 return None
112
113# Create an zone based on the value given
114def create_zone(maas, zone):
115 merged = zone.copy()
116 # merged['op'] = 'new'
117 res = maas.post('/zones/', merged)
118 if res.ok:
119 return { 'error': False, 'status': get_zone(maas, merged['name']) }
120 return { 'error': True, 'status': string_or_object(res.text) }
121
122# Delete an zone based on the name
123def delete_zone(maas, name):
124 res = maas.delete('/zones/%s/' % name)
125 if res.ok:
126 return { 'error': False }
127 return { 'error': True, 'status': string_or_object(res.text) }
128
129def update_zone(maas, have, want):
130 merged = have.copy()
131 merged.update(want)
132 res = put(maas, '/zones/%s/' % merged['name'], merged)
133 if res.ok:
134 return { 'error': False, 'status': get_zone(maas, merged['name']) }
135 return { 'error': True, 'status': string_or_object(res.text) }
136
137def main():
138 module = AnsibleModule(
139 argument_spec = dict(
140 maas=dict(default='http://localhost/MAAS/api/1.0/'),
141 key=dict(required=True),
142 name=dict(required=True),
143 description=dict(required=False),
144 state=dict(default='present', choices=['present', 'absent', 'query'])
145 ),
146 supports_check_mode = False
147 )
148
149 maas = module.params['maas']
150 key = module.params['key']
151 state = module.params['state']
152
153 # Construct a sparsely populate desired state
154 desired = remove_null({
155 'name': module.params['name'],
156 'description': module.params['description'],
157 })
158
159 # Authenticate into MAAS
160 auth = MaasAuth(maas, key)
161 maas = MaasClient(auth)
162
163 # Attempt to get the zone from MAAS
164 zone = get_zone(maas, desired['name'])
165
166 # Actions if the zone does not currently exist
167 if not zone:
168 if state == 'query':
169 # If this is a query, returne it is not found
170 module.exit_json(changed=False, found=False)
171 elif state == 'present':
172 # If this should be present, then attempt to create it
173 res = create_zone(maas, desired)
174 if res['error']:
175 module.fail_json(msg=res['status'])
176 else:
177 module.exit_json(changed=True, zone=res['status'])
178 else:
179 # If this should be absent, then we are done and in the desired state
180 module.exit_json(changed=False)
181
182 # Done with zones does not exists actions
183 return
184
185 # Actions if the zone does exist
186 if state == 'query':
187 # If this is a query, return the zone
188 module.exit_json(changed=False, found=True, zone=zone)
189 return
190 elif state == 'present':
191 # If we want this to exists check to see if this is different and
192 # needs updated
193 if different(zone, desired):
194 res = update_zone(maas, zone, desired)
195 if res['error']:
196 module.fail_json(msg=res['status'])
197 else:
198 module.exit_json(changed=True, zone=res['status'])
199 else:
200 # No differences, to nothing to change
201 module.exit_json(changed=False, zone=zone)
202 else:
203 # If we don't want this zone, then delete it
204 res = delete_zone(maas, zone['name'])
205 if res['error']:
206 module.fail_json(msg=res['status'])
207 else:
208 module.exit_json(changed=True, zone=zone)
209
210# this is magic, see lib/ansible/module_common.py
211#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
212if __name__ == '__main__':
213 main()