1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# (c) 2017, René Moser <mail@renemoser.net>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import (absolute_import, division, print_function)
8__metaclass__ = type
9
10ANSIBLE_METADATA = {'metadata_version': '1.1',
11                    'status': ['preview'],
12                    'supported_by': 'community'}
13
14DOCUMENTATION = r'''
15---
16module: vultr_user
17short_description: Manages users on Vultr.
18description:
19  - Create, update and remove users.
20version_added: "2.5"
21author: "René Moser (@resmo)"
22options:
23  name:
24    description:
25      - Name of the user
26    required: true
27    type: str
28  email:
29    description:
30      - Email of the user.
31      - Required if C(state=present).
32    type: str
33  password:
34    description:
35      - Password of the user.
36      - Only considered while creating a user or when C(force=yes).
37    type: str
38  force:
39    description:
40      - Password will only be changed with enforcement.
41    default: no
42    type: bool
43  api_enabled:
44    description:
45      - Whether the API is enabled or not.
46    default: yes
47    type: bool
48  acls:
49    description:
50      - List of ACLs this users should have, see U(https://www.vultr.com/api/#user_user_list).
51      - Required if C(state=present).
52      - One or more of the choices list, some depend on each other.
53    choices:
54      - manage_users
55      - subscriptions
56      - provisioning
57      - billing
58      - support
59      - abuse
60      - dns
61      - upgrade
62    aliases: [ acl ]
63    type: list
64  state:
65    description:
66      - State of the user.
67    default: present
68    choices: [ present, absent ]
69    type: str
70extends_documentation_fragment: vultr
71'''
72
73EXAMPLES = r'''
74- name: Ensure a user exists
75  local_action:
76    module: vultr_user
77    name: john
78    email: john.doe@example.com
79    password: s3cr3t
80    acls:
81      - upgrade
82      - dns
83      - manage_users
84      - subscriptions
85      - upgrade
86
87- name: Remove a user
88  local_action:
89    module: vultr_user
90    name: john
91    state: absent
92'''
93
94RETURN = r'''
95---
96vultr_api:
97  description: Response from Vultr API with a few additions/modification
98  returned: success
99  type: complex
100  contains:
101    api_account:
102      description: Account used in the ini file to select the key
103      returned: success
104      type: str
105      sample: default
106    api_timeout:
107      description: Timeout used for the API requests
108      returned: success
109      type: int
110      sample: 60
111    api_retries:
112      description: Amount of max retries for the API requests
113      returned: success
114      type: int
115      sample: 5
116    api_retry_max_delay:
117      description: Exponential backoff delay in seconds between retries up to this max delay value.
118      returned: success
119      type: int
120      sample: 12
121      version_added: '2.9'
122    api_endpoint:
123      description: Endpoint used for the API requests
124      returned: success
125      type: str
126      sample: "https://api.vultr.com"
127vultr_user:
128  description: Response from Vultr API
129  returned: success
130  type: complex
131  contains:
132    id:
133      description: ID of the user.
134      returned: success
135      type: str
136      sample: 5904bc6ed9234
137    api_key:
138      description: API key of the user.
139      returned: only after resource was created
140      type: str
141      sample: 567E6K567E6K567E6K567E6K567E6K
142    name:
143      description: Name of the user.
144      returned: success
145      type: str
146      sample: john
147    email:
148      description: Email of the user.
149      returned: success
150      type: str
151      sample: "john@exmaple.com"
152    api_enabled:
153      description: Whether the API is enabled or not.
154      returned: success
155      type: bool
156      sample: true
157    acls:
158      description: List of ACLs of the user.
159      returned: success
160      type: list
161      sample: [manage_users, support, upgrade]
162'''
163
164from ansible.module_utils.basic import AnsibleModule
165from ansible.module_utils.vultr import (
166    Vultr,
167    vultr_argument_spec,
168)
169
170
171ACLS = [
172    'manage_users',
173    'subscriptions',
174    'provisioning',
175    'billing',
176    'support',
177    'abuse',
178    'dns',
179    'upgrade',
180]
181
182
183class AnsibleVultrUser(Vultr):
184
185    def __init__(self, module):
186        super(AnsibleVultrUser, self).__init__(module, "vultr_user")
187
188        self.returns = {
189            'USERID': dict(key='id'),
190            'name': dict(),
191            'email': dict(),
192            'api_enabled': dict(convert_to='bool'),
193            'acls': dict(),
194            'api_key': dict()
195        }
196
197    def _common_args(self):
198        return {
199            'name': self.module.params.get('name'),
200            'email': self.module.params.get('email'),
201            'acls': self.module.params.get('acls'),
202            'password': self.module.params.get('password'),
203            'api_enabled': self.get_yes_or_no('api_enabled'),
204        }
205
206    def get_user(self):
207        users = self.api_query(path="/v1/user/list")
208        for user in users or []:
209            if user.get('name') == self.module.params.get('name'):
210                return user
211        return {}
212
213    def present_user(self):
214        user = self.get_user()
215        if not user:
216            user = self._create_user(user)
217        else:
218            user = self._update_user(user)
219        return user
220
221    def _has_changed(self, user, data):
222        for k, v in data.items():
223            if k not in user:
224                continue
225            elif isinstance(v, list):
226                for i in v:
227                    if i not in user[k]:
228                        return True
229            elif data[k] != user[k]:
230                return True
231        return False
232
233    def _create_user(self, user):
234        self.module.fail_on_missing_params(required_params=['password'])
235
236        self.result['changed'] = True
237
238        data = self._common_args()
239        self.result['diff']['before'] = {}
240        self.result['diff']['after'] = data
241
242        if not self.module.check_mode:
243            user = self.api_query(
244                path="/v1/user/create",
245                method="POST",
246                data=data
247            )
248            user.update(self.get_user())
249        return user
250
251    def _update_user(self, user):
252        data = self._common_args()
253        data.update({
254            'USERID': user['USERID'],
255        })
256
257        force = self.module.params.get('force')
258        if not force:
259            del data['password']
260
261        if force or self._has_changed(user=user, data=data):
262            self.result['changed'] = True
263
264            self.result['diff']['before'] = user
265            self.result['diff']['after'] = user.copy()
266            self.result['diff']['after'].update(data)
267
268            if not self.module.check_mode:
269                self.api_query(
270                    path="/v1/user/update",
271                    method="POST",
272                    data=data
273                )
274                user = self.get_user()
275        return user
276
277    def absent_user(self):
278        user = self.get_user()
279        if user:
280            self.result['changed'] = True
281
282            data = {
283                'USERID': user['USERID'],
284            }
285
286            self.result['diff']['before'] = user
287            self.result['diff']['after'] = {}
288
289            if not self.module.check_mode:
290                self.api_query(
291                    path="/v1/user/delete",
292                    method="POST",
293                    data=data
294                )
295        return user
296
297
298def main():
299    argument_spec = vultr_argument_spec()
300    argument_spec.update(dict(
301        name=dict(type='str', required=True),
302        email=dict(type='str',),
303        password=dict(type='str', no_log=True),
304        force=dict(type='bool', default=False),
305        api_enabled=dict(type='bool', default=True),
306        acls=dict(type='list', choices=ACLS, aliases=['acl']),
307        state=dict(type='str', choices=['present', 'absent'], default='present'),
308    ))
309
310    module = AnsibleModule(
311        argument_spec=argument_spec,
312        required_if=[
313            ('state', 'present', ['email', 'acls']),
314        ],
315        supports_check_mode=True,
316    )
317
318    vultr_user = AnsibleVultrUser(module)
319    if module.params.get('state') == "absent":
320        user = vultr_user.absent_user()
321    else:
322        user = vultr_user.present_user()
323
324    result = vultr_user.get_result(user)
325    module.exit_json(**result)
326
327
328if __name__ == '__main__':
329    main()
330