add kubespray to the XTesting as it provides newer version of kubenetes and can be...
[it/test.git] / XTesting / kubespray / library / kube.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 DOCUMENTATION = """
5 ---
6 module: kube
7 short_description: Manage Kubernetes Cluster
8 description:
9   - Create, replace, remove, and stop resources within a Kubernetes Cluster
10 version_added: "2.0"
11 options:
12   name:
13     required: false
14     default: null
15     description:
16       - The name associated with resource
17   filename:
18     required: false
19     default: null
20     description:
21       - The path and filename of the resource(s) definition file(s).
22       - To operate on several files this can accept a comma separated list of files or a list of files.
23     aliases: [ 'files', 'file', 'filenames' ]
24   kubectl:
25     required: false
26     default: null
27     description:
28       - The path to the kubectl bin
29   namespace:
30     required: false
31     default: null
32     description:
33       - The namespace associated with the resource(s)
34   resource:
35     required: false
36     default: null
37     description:
38       - The resource to perform an action on. pods (po), replicationControllers (rc), services (svc)
39   label:
40     required: false
41     default: null
42     description:
43       - The labels used to filter specific resources.
44   server:
45     required: false
46     default: null
47     description:
48       - The url for the API server that commands are executed against.
49   force:
50     required: false
51     default: false
52     description:
53       - A flag to indicate to force delete, replace, or stop.
54   wait:
55     required: false
56     default: false
57     description:
58       - A flag to indicate to wait for resources to be created before continuing to the next step
59   all:
60     required: false
61     default: false
62     description:
63       - A flag to indicate delete all, stop all, or all namespaces when checking exists.
64   log_level:
65     required: false
66     default: 0
67     description:
68       - Indicates the level of verbosity of logging by kubectl.
69   state:
70     required: false
71     choices: ['present', 'absent', 'latest', 'reloaded', 'stopped']
72     default: present
73     description:
74       - present handles checking existence or creating if definition file provided,
75         absent handles deleting resource(s) based on other options,
76         latest handles creating or updating based on existence,
77         reloaded handles updating resource(s) definition using definition file,
78         stopped handles stopping resource(s) based on other options.
79   recursive:
80     required: false
81     default: false
82     description:
83       - Process the directory used in -f, --filename recursively.
84         Useful when you want to manage related manifests organized
85         within the same directory.
86 requirements:
87   - kubectl
88 author: "Kenny Jones (@kenjones-cisco)"
89 """
90
91 EXAMPLES = """
92 - name: test nginx is present
93   kube: name=nginx resource=rc state=present
94
95 - name: test nginx is stopped
96   kube: name=nginx resource=rc state=stopped
97
98 - name: test nginx is absent
99   kube: name=nginx resource=rc state=absent
100
101 - name: test nginx is present
102   kube: filename=/tmp/nginx.yml
103
104 - name: test nginx and postgresql are present
105   kube: files=/tmp/nginx.yml,/tmp/postgresql.yml
106
107 - name: test nginx and postgresql are present
108   kube:
109     files:
110       - /tmp/nginx.yml
111       - /tmp/postgresql.yml
112 """
113
114
115 class KubeManager(object):
116
117     def __init__(self, module):
118
119         self.module = module
120
121         self.kubectl = module.params.get('kubectl')
122         if self.kubectl is None:
123             self.kubectl =  module.get_bin_path('kubectl', True)
124         self.base_cmd = [self.kubectl]
125
126         if module.params.get('server'):
127             self.base_cmd.append('--server=' + module.params.get('server'))
128
129         if module.params.get('log_level'):
130             self.base_cmd.append('--v=' + str(module.params.get('log_level')))
131
132         if module.params.get('namespace'):
133             self.base_cmd.append('--namespace=' + module.params.get('namespace'))
134
135
136         self.all = module.params.get('all')
137         self.force = module.params.get('force')
138         self.wait = module.params.get('wait')
139         self.name = module.params.get('name')
140         self.filename = [f.strip() for f in module.params.get('filename') or []]
141         self.resource = module.params.get('resource')
142         self.label = module.params.get('label')
143         self.recursive = module.params.get('recursive')
144
145     def _execute(self, cmd):
146         args = self.base_cmd + cmd
147         try:
148             rc, out, err = self.module.run_command(args)
149             if rc != 0:
150                 self.module.fail_json(
151                     msg='error running kubectl (%s) command (rc=%d), out=\'%s\', err=\'%s\'' % (' '.join(args), rc, out, err))
152         except Exception as exc:
153             self.module.fail_json(
154                 msg='error running kubectl (%s) command: %s' % (' '.join(args), str(exc)))
155         return out.splitlines()
156
157     def _execute_nofail(self, cmd):
158         args = self.base_cmd + cmd
159         rc, out, err = self.module.run_command(args)
160         if rc != 0:
161             return None
162         return out.splitlines()
163
164     def create(self, check=True, force=True):
165         if check and self.exists():
166             return []
167
168         cmd = ['apply']
169
170         if force:
171             cmd.append('--force')
172
173         if self.wait:
174             cmd.append('--wait')
175
176         if self.recursive:
177             cmd.append('--recursive={}'.format(self.recursive))
178
179         if not self.filename:
180             self.module.fail_json(msg='filename required to create')
181
182         cmd.append('--filename=' + ','.join(self.filename))
183
184         return self._execute(cmd)
185
186     def replace(self, force=True):
187
188         cmd = ['apply']
189
190         if force:
191             cmd.append('--force')
192
193         if self.wait:
194             cmd.append('--wait')
195
196         if self.recursive:
197             cmd.append('--recursive={}'.format(self.recursive))
198
199         if not self.filename:
200             self.module.fail_json(msg='filename required to reload')
201
202         cmd.append('--filename=' + ','.join(self.filename))
203
204         return self._execute(cmd)
205
206     def delete(self):
207
208         if not self.force and not self.exists():
209             return []
210
211         cmd = ['delete']
212
213         if self.filename:
214             cmd.append('--filename=' + ','.join(self.filename))
215             if self.recursive:
216                 cmd.append('--recursive={}'.format(self.recursive))
217         else:
218             if not self.resource:
219                 self.module.fail_json(msg='resource required to delete without filename')
220
221             cmd.append(self.resource)
222
223             if self.name:
224                 cmd.append(self.name)
225
226             if self.label:
227                 cmd.append('--selector=' + self.label)
228
229             if self.all:
230                 cmd.append('--all')
231
232             if self.force:
233                 cmd.append('--ignore-not-found')
234
235             if self.recursive:
236                 cmd.append('--recursive={}'.format(self.recursive))
237
238         return self._execute(cmd)
239
240     def exists(self):
241         cmd = ['get']
242
243         if self.filename:
244             cmd.append('--filename=' + ','.join(self.filename))
245             if self.recursive:
246                 cmd.append('--recursive={}'.format(self.recursive))
247         else:
248             if not self.resource:
249                 self.module.fail_json(msg='resource required without filename')
250
251             cmd.append(self.resource)
252
253             if self.name:
254                 cmd.append(self.name)
255
256             if self.label:
257                 cmd.append('--selector=' + self.label)
258
259             if self.all:
260                 cmd.append('--all-namespaces')
261
262         cmd.append('--no-headers')
263
264         result = self._execute_nofail(cmd)
265         if not result:
266             return False
267         return True
268
269     # TODO: This is currently unused, perhaps convert to 'scale' with a replicas param?
270     def stop(self):
271
272         if not self.force and not self.exists():
273             return []
274
275         cmd = ['stop']
276
277         if self.filename:
278             cmd.append('--filename=' + ','.join(self.filename))
279             if self.recursive:
280                 cmd.append('--recursive={}'.format(self.recursive))
281         else:
282             if not self.resource:
283                 self.module.fail_json(msg='resource required to stop without filename')
284
285             cmd.append(self.resource)
286
287             if self.name:
288                 cmd.append(self.name)
289
290             if self.label:
291                 cmd.append('--selector=' + self.label)
292
293             if self.all:
294                 cmd.append('--all')
295
296             if self.force:
297                 cmd.append('--ignore-not-found')
298
299         return self._execute(cmd)
300
301
302 def main():
303
304     module = AnsibleModule(
305         argument_spec=dict(
306             name=dict(),
307             filename=dict(type='list', aliases=['files', 'file', 'filenames']),
308             namespace=dict(),
309             resource=dict(),
310             label=dict(),
311             server=dict(),
312             kubectl=dict(),
313             force=dict(default=False, type='bool'),
314             wait=dict(default=False, type='bool'),
315             all=dict(default=False, type='bool'),
316             log_level=dict(default=0, type='int'),
317             state=dict(default='present', choices=['present', 'absent', 'latest', 'reloaded', 'stopped', 'exists']),
318             recursive=dict(default=False, type='bool'),
319             ),
320             mutually_exclusive=[['filename', 'list']]
321         )
322
323     changed = False
324
325     manager = KubeManager(module)
326     state = module.params.get('state')
327     if state == 'present':
328         result = manager.create(check=False)
329
330     elif state == 'absent':
331         result = manager.delete()
332
333     elif state == 'reloaded':
334         result = manager.replace()
335
336     elif state == 'stopped':
337         result = manager.stop()
338
339     elif state == 'latest':
340         result = manager.replace()
341
342     elif state == 'exists':
343         result = manager.exists()
344         module.exit_json(changed=changed,
345                      msg='%s' % result)
346
347     else:
348         module.fail_json(msg='Unrecognized state %s.' % state)
349
350     module.exit_json(changed=changed,
351                      msg='success: %s' % (' '.join(result))
352                      )
353
354
355 from ansible.module_utils.basic import *  # noqa
356 if __name__ == '__main__':
357     main()