From 2d122d692bf14b939dcaefd4ca46d018a198eb56 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Wed, 18 Jan 2017 15:50:08 -0500 Subject: Adding oc_obj to the lib_openshift library --- roles/lib_openshift/src/ansible/oc_obj.py | 37 ++++++ roles/lib_openshift/src/class/oc_obj.py | 193 ++++++++++++++++++++++++++++++ roles/lib_openshift/src/doc/obj | 95 +++++++++++++++ roles/lib_openshift/src/lib/base.py | 24 +++- roles/lib_openshift/src/sources.yml | 27 +++-- 5 files changed, 361 insertions(+), 15 deletions(-) create mode 100644 roles/lib_openshift/src/ansible/oc_obj.py create mode 100644 roles/lib_openshift/src/class/oc_obj.py create mode 100644 roles/lib_openshift/src/doc/obj (limited to 'roles/lib_openshift/src') diff --git a/roles/lib_openshift/src/ansible/oc_obj.py b/roles/lib_openshift/src/ansible/oc_obj.py new file mode 100644 index 000000000..701740e4f --- /dev/null +++ b/roles/lib_openshift/src/ansible/oc_obj.py @@ -0,0 +1,37 @@ +# pylint: skip-file +# flake8: noqa + +# pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for services + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + all_namespaces=dict(defaul=False, type='bool'), + name=dict(default=None, type='str'), + files=dict(default=None, type='list'), + kind=dict(required=True, type='str'), + delete_after=dict(default=False, type='bool'), + content=dict(default=None, type='dict'), + force=dict(default=False, type='bool'), + selector=dict(default=None, type='str'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + rval = OCObject.run_ansible(module.params, module.check_mode) + if 'failed' in rval: + module.fail_json(**rval) + + module.exit_json(**rval) + +if __name__ == '__main__': + main() diff --git a/roles/lib_openshift/src/class/oc_obj.py b/roles/lib_openshift/src/class/oc_obj.py new file mode 100644 index 000000000..9d0b8e45b --- /dev/null +++ b/roles/lib_openshift/src/class/oc_obj.py @@ -0,0 +1,193 @@ +# pylint: skip-file +# flake8: noqa + +# pylint: disable=too-many-instance-attributes +class OCObject(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + kind, + namespace, + rname=None, + selector=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False, + all_namespaces=False): + ''' Constructor for OpenshiftOC ''' + super(OCObject, self).__init__(namespace, kubeconfig, + all_namespaces=all_namespaces) + self.kind = kind + self.namespace = namespace + self.name = rname + self.selector = selector + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a kind by name ''' + results = self._get(self.kind, rname=self.name, selector=self.selector) + if results['returncode'] != 0 and 'stderr' in results and \ + '\"%s\" not found' % self.name in results['stderr']: + results['returncode'] = 0 + + return results + + def delete(self): + '''return all pods ''' + return self._delete(self.kind, self.name) + + def create(self, files=None, content=None): + ''' + Create a config + + NOTE: This creates the first file OR the first conent. + TODO: Handle all files and content passed in + ''' + if files: + return self._create(files[0]) + + content['data'] = yaml.dump(content['data']) + content_file = Utils.create_files_from_contents(content)[0] + + return self._create(content_file['path']) + + # pylint: disable=too-many-function-args + def update(self, files=None, content=None, force=False): + '''update a current openshift object + + This receives a list of file names or content + and takes the first and calls replace. + + TODO: take an entire list + ''' + if files: + return self._replace(files[0], force) + + if content and 'data' in content: + content = content['data'] + + return self.update_content(content, force) + + def update_content(self, content, force=False): + '''update an object through using the content param''' + return self._replace_content(self.kind, self.name, content, force=force) + + def needs_update(self, files=None, content=None, content_type='yaml'): + ''' check to see if we need to update ''' + objects = self.get() + if objects['returncode'] != 0: + return objects + + # pylint: disable=no-member + data = None + if files: + data = Utils.get_resource_file(files[0], content_type) + elif content and 'data' in content: + data = content['data'] + else: + data = content + + # if equal then no need. So not equal is True + return not Utils.check_def_equal(data, objects['results'][0], skip_keys=None, debug=False) + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params, check_mode=False): + '''perform the ansible idempotent code''' + + ocobj = OCObject(params['kind'], + params['namespace'], + params['name'], + params['selector'], + kubeconfig=params['kubeconfig'], + verbose=params['debug'], + all_namespaces=params['all_namespaces']) + + state = params['state'] + + api_rval = ocobj.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval, 'state': 'list'} + + if not params['name']: + return {'failed': True, 'msg': 'Please specify a name when state is absent|present.'} # noqa: E501 + + ######## + # Delete + ######## + if state == 'absent': + if not Utils.exists(api_rval['results'], params['name']): + return {'changed': False, 'state': 'absent'} + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete'} + + api_rval = ocobj.delete() + + return {'changed': True, 'results': api_rval, 'state': 'absent'} + + if state == 'present': + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], params['name']): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create'} + + # Create it here + api_rval = ocobj.create(params['files'], params['content']) + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # Remove files + if params['files'] and params['delete_after']: + Utils.cleanup(params['files']) + + return {'changed': True, 'results': api_rval, 'state': "present"} + + ######## + # Update + ######## + # if a file path is passed, use it. + update = ocobj.needs_update(params['files'], params['content']) + if not isinstance(update, bool): + return {'failed': True, 'msg': update} + + # No changes + if not update: + if params['files'] and params['delete_after']: + Utils.cleanup(params['files']) + + return {'changed': False, 'results': api_rval['results'][0], 'state': "present"} + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'} + + api_rval = ocobj.update(params['files'], + params['content'], + params['force']) + + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': "present"} diff --git a/roles/lib_openshift/src/doc/obj b/roles/lib_openshift/src/doc/obj new file mode 100644 index 000000000..e44843eb3 --- /dev/null +++ b/roles/lib_openshift/src/doc/obj @@ -0,0 +1,95 @@ +# flake8: noqa +# pylint: skip-file + +DOCUMENTATION = ''' +--- +module: oc_obj +short_description: Generic interface to openshift objects +description: + - Manage openshift objects programmatically. +options: + state: + description: + - Currently present is only supported state. + required: true + default: present + choices: ["present", "absent", "list"] + aliases: [] + kubeconfig: + description: + - The path for the kubeconfig file to use for authentication + required: false + default: /etc/origin/master/admin.kubeconfig + aliases: [] + debug: + description: + - Turn on debug output. + required: false + default: False + aliases: [] + name: + description: + - Name of the object that is being queried. + required: false + default: None + aliases: [] + namespace: + description: + - The namespace where the object lives. + required: false + default: str + aliases: [] + all_namespace: + description: + - The namespace where the object lives. + required: false + default: false + aliases: [] + kind: + description: + - The kind attribute of the object. e.g. dc, bc, svc, route + required: True + default: None + aliases: [] + files: + description: + - A list of files provided for object + required: false + default: None + aliases: [] + delete_after: + description: + - Whether or not to delete the files after processing them. + required: false + default: false + aliases: [] + content: + description: + - Content of the object being managed. + required: false + default: None + aliases: [] + force: + description: + - Whether or not to force the operation + required: false + default: None + aliases: [] + selector: + description: + - Selector that gets added to the query. + required: false + default: None + aliases: [] +author: +- "Kenny Woodson " +extends_documentation_fragment: [] +''' + +EXAMPLES = ''' +oc_obj: + kind: dc + name: router + namespace: default +register: router_output +''' diff --git a/roles/lib_openshift/src/lib/base.py b/roles/lib_openshift/src/lib/base.py index 915a7caca..cf2f1b14d 100644 --- a/roles/lib_openshift/src/lib/base.py +++ b/roles/lib_openshift/src/lib/base.py @@ -47,14 +47,14 @@ class OpenShiftCLI(object): return {'returncode': 0, 'updated': False} def _replace(self, fname, force=False): - '''return all pods ''' + '''replace the current object with oc replace''' cmd = ['-n', self.namespace, 'replace', '-f', fname] if force: cmd.append('--force') return self.openshift_cmd(cmd) def _create_from_content(self, rname, content): - '''return all pods ''' + '''create a temporary file and then call oc create on it''' fname = '/tmp/%s' % rname yed = Yedit(fname, content=content) yed.write() @@ -64,11 +64,11 @@ class OpenShiftCLI(object): return self._create(fname) def _create(self, fname): - '''return all pods ''' + '''call oc create on a filename''' return self.openshift_cmd(['create', '-f', fname, '-n', self.namespace]) def _delete(self, resource, rname, selector=None): - '''return all pods ''' + '''call oc delete on a resource''' cmd = ['delete', resource, rname, '-n', self.namespace] if selector: cmd.append('--selector=%s' % selector) @@ -76,7 +76,14 @@ class OpenShiftCLI(object): return self.openshift_cmd(cmd) def _process(self, template_name, create=False, params=None, template_data=None): # noqa: E501 - '''return all pods ''' + '''process a template + + template_name: the name of the template to process + create: whether to send to oc create after processing + params: the parameters for the template + template_data: the incoming template's data; instead of a file + ''' + cmd = ['process', '-n', self.namespace] if template_data: cmd.extend(['-f', '-']) @@ -138,7 +145,12 @@ class OpenShiftCLI(object): return self.openshift_cmd(cmd, oadm=True, output=True, output_type='raw') # noqa: E501 def _list_pods(self, node=None, selector=None, pod_selector=None): - ''' perform oadm manage-node evacuate ''' + ''' perform oadm list pods + + node: the node in which to list pods + selector: the label selector filter if provided + pod_selector: the pod selector filter if provided + ''' cmd = ['manage-node'] if node: cmd.extend(node) diff --git a/roles/lib_openshift/src/sources.yml b/roles/lib_openshift/src/sources.yml index 08fbbc201..50d70d1e5 100644 --- a/roles/lib_openshift/src/sources.yml +++ b/roles/lib_openshift/src/sources.yml @@ -1,20 +1,29 @@ --- -oc_route.py: +oc_edit.py: - doc/generated - doc/license - lib/import.py -- doc/route +- doc/edit - ../../lib_utils/src/class/yedit.py - lib/base.py -- lib/route.py -- class/oc_route.py -- ansible/oc_route.py -oc_edit.py: +- class/oc_edit.py +- ansible/oc_edit.py +oc_obj.py: - doc/generated - doc/license - lib/import.py -- doc/edit +- doc/obj - ../../lib_utils/src/class/yedit.py - lib/base.py -- class/oc_edit.py -- ansible/oc_edit.py +- class/oc_obj.py +- ansible/oc_obj.py +oc_route.py: +- doc/generated +- doc/license +- lib/import.py +- doc/route +- ../../lib_utils/src/class/yedit.py +- lib/base.py +- lib/route.py +- class/oc_route.py +- ansible/oc_route.py -- cgit v1.2.3