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