1#!/usr/bin/python
2
3# Copyright (c) 2018 Catalyst IT Ltd.
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9
10ANSIBLE_METADATA = {'metadata_version': '1.1',
11                    'status': ['preview'],
12                    'supported_by': 'community'}
13
14
15DOCUMENTATION = '''
16---
17module: os_coe_cluster
18short_description: Add/Remove COE cluster from OpenStack Cloud
19extends_documentation_fragment: openstack
20version_added: "2.8"
21author: "Feilong Wang (@flwang)"
22description:
23   - Add or Remove COE cluster from the OpenStack Container Infra service.
24options:
25   availability_zone:
26     description:
27       - Ignored. Present for backwards compatibility
28   cluster_template_id:
29     description:
30        - The template ID of cluster template.
31     required: true
32   discovery_url:
33       description:
34         - Url used for cluster node discovery
35   docker_volume_size:
36      description:
37         - The size in GB of the docker volume
38   flavor_id:
39      description:
40         - The flavor of the minion node for this ClusterTemplate
41   keypair:
42      description:
43         - Name of the keypair to use.
44   labels:
45      description:
46         - One or more key/value pairs
47   master_flavor_id:
48      description:
49         - The flavor of the master node for this ClusterTemplate
50   master_count:
51      description:
52         - The number of master nodes for this cluster
53      default: 1
54   name:
55      description:
56         - Name that has to be given to the cluster template
57      required: true
58   node_count:
59      description:
60         - The number of nodes for this cluster
61      default: 1
62   state:
63      description:
64         - Indicate desired state of the resource.
65      choices: [present, absent]
66      default: present
67   timeout:
68      description:
69         - Timeout for creating the cluster in minutes. Default to 60 mins
70           if not set
71      default: 60
72requirements: ["openstacksdk"]
73'''
74
75RETURN = '''
76id:
77    description: The cluster UUID.
78    returned: On success when I(state) is 'present'
79    type: str
80    sample: "39007a7e-ee4f-4d13-8283-b4da2e037c69"
81cluster:
82    description: Dictionary describing the cluster.
83    returned: On success when I(state) is 'present'
84    type: complex
85    contains:
86      api_address:
87          description:
88            - Api address of cluster master node
89          type: str
90          sample: https://172.24.4.30:6443
91      cluster_template_id:
92          description: The cluster_template UUID
93          type: str
94          sample: '7b1418c8-cea8-48fc-995d-52b66af9a9aa'
95      coe_version:
96          description:
97            - Version of the COE software currently running in this cluster
98          type: str
99          sample: v1.11.1
100      container_version:
101          description:
102            - "Version of the container software. Example: docker version."
103          type: str
104          sample: 1.12.6
105      created_at:
106          description:
107            - The time in UTC at which the cluster is created
108          type: str
109          sample: "2018-08-16T10:29:45+00:00"
110      create_timeout:
111          description:
112            - Timeout for creating the cluster in minutes. Default to 60 if
113              not set.
114          type: int
115          sample: 60
116      discovery_url:
117          description:
118            - Url used for cluster node discovery
119          type: str
120          sample: https://discovery.etcd.io/a42ee38e7113f31f4d6324f24367aae5
121      faults:
122          description:
123            - Fault info collected from the Heat resources of this cluster
124          type: dict
125          sample: {'0': 'ResourceInError: resources[0].resources...'}
126      flavor_id:
127          description:
128            - The flavor of the minion node for this cluster
129          type: str
130          sample: c1.c1r1
131      keypair:
132          description:
133            - Name of the keypair to use.
134          type: str
135          sample: mykey
136      labels:
137          description: One or more key/value pairs
138          type: dict
139          sample: {'key1': 'value1', 'key2': 'value2'}
140      master_addresses:
141          description:
142            - IP addresses of cluster master nodes
143          type: list
144          sample: ['172.24.4.5']
145      master_count:
146          description:
147            - The number of master nodes for this cluster.
148          type: int
149          sample: 1
150      master_flavor_id:
151          description:
152            - The flavor of the master node for this cluster
153          type: str
154          sample: c1.c1r1
155      name:
156          description:
157            - Name that has to be given to the cluster
158          type: str
159          sample: k8scluster
160      node_addresses:
161          description:
162            - IP addresses of cluster slave nodes
163          type: list
164          sample: ['172.24.4.8']
165      node_count:
166          description:
167            - The number of master nodes for this cluster.
168          type: int
169          sample: 1
170      stack_id:
171          description:
172            - Stack id of the Heat stack
173          type: str
174          sample: '07767ec6-85f5-44cb-bd63-242a8e7f0d9d'
175      status:
176          description: Status of the cluster from the heat stack
177          type: str
178          sample: 'CREATE_COMLETE'
179      status_reason:
180          description:
181            - Status reason of the cluster from the heat stack
182          type: str
183          sample: 'Stack CREATE completed successfully'
184      updated_at:
185          description:
186            - The time in UTC at which the cluster is updated
187          type: str
188          sample: '2018-08-16T10:39:25+00:00'
189      uuid:
190          description:
191            - Unique UUID for this cluster
192          type: str
193          sample: '86246a4d-a16c-4a58-9e96ad7719fe0f9d'
194'''
195
196EXAMPLES = '''
197# Create a new Kubernetes cluster
198- os_coe_cluster:
199    name: k8s
200    cluster_template_id: k8s-ha
201    keypair: mykey
202    master_count: 3
203    node_count: 5
204'''
205
206from ansible.module_utils.basic import AnsibleModule
207from ansible.module_utils.openstack import openstack_full_argument_spec, openstack_module_kwargs, openstack_cloud_from_module
208
209
210def _parse_labels(labels):
211    if isinstance(labels, str):
212        labels_dict = {}
213        for kv_str in labels.split(","):
214            k, v = kv_str.split("=")
215            labels_dict[k] = v
216        return labels_dict
217    if not labels:
218        return {}
219    return labels
220
221
222def main():
223    argument_spec = openstack_full_argument_spec(
224        cluster_template_id=dict(required=True),
225        discovery_url=dict(default=None),
226        docker_volume_size=dict(type='int'),
227        flavor_id=dict(default=None),
228        keypair=dict(default=None),
229        labels=dict(default=None, type='raw'),
230        master_count=dict(type='int', default=1),
231        master_flavor_id=dict(default=None),
232        name=dict(required=True),
233        node_count=dict(type='int', default=1),
234        state=dict(default='present', choices=['absent', 'present']),
235        timeout=dict(type='int', default=60),
236    )
237    module_kwargs = openstack_module_kwargs()
238    module = AnsibleModule(argument_spec, **module_kwargs)
239
240    params = module.params.copy()
241
242    state = module.params['state']
243    name = module.params['name']
244    cluster_template_id = module.params['cluster_template_id']
245
246    kwargs = dict(
247        discovery_url=module.params['discovery_url'],
248        docker_volume_size=module.params['docker_volume_size'],
249        flavor_id=module.params['flavor_id'],
250        keypair=module.params['keypair'],
251        labels=_parse_labels(params['labels']),
252        master_count=module.params['master_count'],
253        master_flavor_id=module.params['master_flavor_id'],
254        node_count=module.params['node_count'],
255        create_timeout=module.params['timeout'],
256    )
257
258    sdk, cloud = openstack_cloud_from_module(module)
259    try:
260        changed = False
261        cluster = cloud.get_coe_cluster(name_or_id=name, filters={'cluster_template_id': cluster_template_id})
262
263        if state == 'present':
264            if not cluster:
265                cluster = cloud.create_coe_cluster(name, cluster_template_id=cluster_template_id, **kwargs)
266                changed = True
267            else:
268                changed = False
269
270            module.exit_json(changed=changed, cluster=cluster, id=cluster['uuid'])
271        elif state == 'absent':
272            if not cluster:
273                module.exit_json(changed=False)
274            else:
275                cloud.delete_coe_cluster(name)
276                module.exit_json(changed=True)
277    except sdk.exceptions.OpenStackCloudException as e:
278        module.fail_json(msg=str(e), extra_data=e.extra_data)
279
280
281if __name__ == "__main__":
282    main()
283