diff options
Diffstat (limited to 'roles/lib_openshift/src/class')
17 files changed, 1339 insertions, 86 deletions
diff --git a/roles/lib_openshift/src/class/oc_adm_ca_server_cert.py b/roles/lib_openshift/src/class/oc_adm_ca_server_cert.py index 18c69f2fa..f954f40ef 100644 --- a/roles/lib_openshift/src/class/oc_adm_ca_server_cert.py +++ b/roles/lib_openshift/src/class/oc_adm_ca_server_cert.py @@ -78,6 +78,9 @@ class CAServerCert(OpenShiftCLI): if proc.returncode == 0: regex = re.compile(r"^\s*X509v3 Subject Alternative Name:\s*?\n\s*(.*)\s*\n", re.MULTILINE) match = regex.search(x509output) # E501 + if not match: + return False + for entry in re.split(r", *", match.group(1)): if entry.startswith('DNS') or entry.startswith('IP Address'): cert_names.append(entry.split(':')[1]) @@ -102,6 +105,7 @@ class CAServerCert(OpenShiftCLI): 'signer_cert': {'value': params['signer_cert'], 'include': True}, 'signer_key': {'value': params['signer_key'], 'include': True}, 'signer_serial': {'value': params['signer_serial'], 'include': True}, + 'expire_days': {'value': params['expire_days'], 'include': True}, 'backup': {'value': params['backup'], 'include': False}, }) @@ -123,7 +127,7 @@ class CAServerCert(OpenShiftCLI): api_rval = server_cert.create() if api_rval['returncode'] != 0: - return {'Failed': True, 'msg': api_rval} + return {'failed': True, 'msg': api_rval} return {'changed': True, 'results': api_rval, 'state': state} diff --git a/roles/lib_openshift/src/class/oadm_manage_node.py b/roles/lib_openshift/src/class/oc_adm_manage_node.py index c07320477..6d9f24baa 100644 --- a/roles/lib_openshift/src/class/oadm_manage_node.py +++ b/roles/lib_openshift/src/class/oc_adm_manage_node.py @@ -44,7 +44,7 @@ class ManageNode(OpenShiftCLI): if selector: _sel = selector - results = self._get('node', rname=_node, selector=_sel) + results = self._get('node', name=_node, selector=_sel) if results['returncode'] != 0: return results diff --git a/roles/lib_openshift/src/class/oc_adm_policy_group.py b/roles/lib_openshift/src/class/oc_adm_policy_group.py index afb066c77..1e51913e0 100644 --- a/roles/lib_openshift/src/class/oc_adm_policy_group.py +++ b/roles/lib_openshift/src/class/oc_adm_policy_group.py @@ -41,6 +41,28 @@ class PolicyGroup(OpenShiftCLI): self.verbose = verbose self._rolebinding = None self._scc = None + self._cluster_policy_bindings = None + self._policy_bindings = None + + @property + def policybindings(self): + if self._policy_bindings is None: + results = self._get('clusterpolicybindings', None) + if results['returncode'] != 0: + raise OpenShiftCLIError('Could not retrieve policybindings') + self._policy_bindings = results['results'][0]['items'][0] + + return self._policy_bindings + + @property + def clusterpolicybindings(self): + if self._cluster_policy_bindings is None: + results = self._get('clusterpolicybindings', None) + if results['returncode'] != 0: + raise OpenShiftCLIError('Could not retrieve clusterpolicybindings') + self._cluster_policy_bindings = results['results'][0]['items'][0] + + return self._cluster_policy_bindings @property def role_binding(self): @@ -81,18 +103,24 @@ class PolicyGroup(OpenShiftCLI): def exists_role_binding(self): ''' return whether role_binding exists ''' - results = self.get() - if results['returncode'] == 0: - self.role_binding = RoleBinding(results['results'][0]) - if self.role_binding.find_group_name(self.config.config_options['group']['value']) != None: - return True + bindings = None + if self.config.config_options['resource_kind']['value'] == 'cluster-role': + bindings = self.clusterpolicybindings + else: + bindings = self.policybindings + if bindings is None: return False - elif self.config.config_options['name']['value'] in results['stderr'] and '" not found' in results['stderr']: - return False + for binding in bindings['roleBindings']: + _rb = binding['roleBinding'] + if _rb['roleRef']['name'] == self.config.config_options['name']['value'] and \ + _rb['groupNames'] is not None and \ + self.config.config_options['group']['value'] in _rb['groupNames']: + self.role_binding = binding + return True - return results + return False def exists_scc(self): ''' return whether scc exists ''' diff --git a/roles/lib_openshift/src/class/oc_adm_policy_user.py b/roles/lib_openshift/src/class/oc_adm_policy_user.py index c9d53acfa..88fcc1ddc 100644 --- a/roles/lib_openshift/src/class/oc_adm_policy_user.py +++ b/roles/lib_openshift/src/class/oc_adm_policy_user.py @@ -40,6 +40,28 @@ class PolicyUser(OpenShiftCLI): self.verbose = verbose self._rolebinding = None self._scc = None + self._cluster_policy_bindings = None + self._policy_bindings = None + + @property + def policybindings(self): + if self._policy_bindings is None: + results = self._get('clusterpolicybindings', None) + if results['returncode'] != 0: + raise OpenShiftCLIError('Could not retrieve policybindings') + self._policy_bindings = results['results'][0]['items'][0] + + return self._policy_bindings + + @property + def clusterpolicybindings(self): + if self._cluster_policy_bindings is None: + results = self._get('clusterpolicybindings', None) + if results['returncode'] != 0: + raise OpenShiftCLIError('Could not retrieve clusterpolicybindings') + self._cluster_policy_bindings = results['results'][0]['items'][0] + + return self._cluster_policy_bindings @property def role_binding(self): @@ -62,36 +84,37 @@ class PolicyUser(OpenShiftCLI): self._scc = scc def get(self): - '''fetch the desired kind''' + '''fetch the desired kind + + This is only used for scc objects. + The {cluster}rolebindings happen in exists. + ''' resource_name = self.config.config_options['name']['value'] if resource_name == 'cluster-reader': resource_name += 's' - # oc adm policy add-... creates policy bindings with the name - # "[resource_name]-binding", however some bindings in the system - # simply use "[resource_name]". So try both. - - results = self._get(self.config.kind, resource_name) - if results['returncode'] == 0: - return results - - # Now try -binding naming convention - return self._get(self.config.kind, resource_name + "-binding") + return self._get(self.config.kind, resource_name) def exists_role_binding(self): ''' return whether role_binding exists ''' - results = self.get() - if results['returncode'] == 0: - self.role_binding = RoleBinding(results['results'][0]) - if self.role_binding.find_user_name(self.config.config_options['user']['value']) != None: - return True + bindings = None + if self.config.config_options['resource_kind']['value'] == 'cluster-role': + bindings = self.clusterpolicybindings + else: + bindings = self.policybindings + if bindings is None: return False - elif self.config.config_options['name']['value'] in results['stderr'] and '" not found' in results['stderr']: - return False + for binding in bindings['roleBindings']: + _rb = binding['roleBinding'] + if _rb['roleRef']['name'] == self.config.config_options['name']['value'] and \ + _rb['userNames'] is not None and \ + self.config.config_options['user']['value'] in _rb['userNames']: + self.role_binding = binding + return True - return results + return False def exists_scc(self): ''' return whether scc exists ''' diff --git a/roles/lib_openshift/src/class/oc_adm_registry.py b/roles/lib_openshift/src/class/oc_adm_registry.py index c083cd179..720b44cdc 100644 --- a/roles/lib_openshift/src/class/oc_adm_registry.py +++ b/roles/lib_openshift/src/class/oc_adm_registry.py @@ -87,8 +87,8 @@ class Registry(OpenShiftCLI): ''' prepared_registry property ''' if not self.__prepared_registry: results = self.prepare_registry() - if not results: - raise RegistryException('Could not perform registry preparation.') + if not results or ('returncode' in results and results['returncode'] != 0): + raise RegistryException('Could not perform registry preparation. {}'.format(results)) self.__prepared_registry = results return self.__prepared_registry @@ -105,7 +105,7 @@ class Registry(OpenShiftCLI): rval = 0 for part in self.registry_parts: - result = self._get(part['kind'], rname=part['name']) + result = self._get(part['kind'], name=part['name']) if result['returncode'] == 0 and part['kind'] == 'dc': self.deploymentconfig = DeploymentConfig(result['results'][0]) elif result['returncode'] == 0 and part['kind'] == 'svc': @@ -153,8 +153,8 @@ class Registry(OpenShiftCLI): # probably need to parse this # pylint thinks results is a string # pylint: disable=no-member - if results['returncode'] != 0 and 'items' in results['results']: - return results + if results['returncode'] != 0 and 'items' not in results['results']: + raise RegistryException('Could not perform registry preparation. {}'.format(results)) service = None deploymentconfig = None diff --git a/roles/lib_openshift/src/class/oc_adm_router.py b/roles/lib_openshift/src/class/oc_adm_router.py index 356d06fdf..1a0b94b80 100644 --- a/roles/lib_openshift/src/class/oc_adm_router.py +++ b/roles/lib_openshift/src/class/oc_adm_router.py @@ -136,7 +136,7 @@ class Router(OpenShiftCLI): self.secret = None self.rolebinding = None for part in self.router_parts: - result = self._get(part['kind'], rname=part['name']) + result = self._get(part['kind'], name=part['name']) if result['returncode'] == 0 and part['kind'] == 'dc': self.deploymentconfig = DeploymentConfig(result['results'][0]) elif result['returncode'] == 0 and part['kind'] == 'svc': diff --git a/roles/lib_openshift/src/class/oc_clusterrole.py b/roles/lib_openshift/src/class/oc_clusterrole.py new file mode 100644 index 000000000..1d3d977db --- /dev/null +++ b/roles/lib_openshift/src/class/oc_clusterrole.py @@ -0,0 +1,163 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCClusterRole(OpenShiftCLI): + ''' Class to manage clusterrole objects''' + kind = 'clusterrole' + + def __init__(self, + name, + rules=None, + kubeconfig=None, + verbose=False): + ''' Constructor for OCClusterRole ''' + super(OCClusterRole, self).__init__(None, kubeconfig=kubeconfig, verbose=verbose) + self.verbose = verbose + self.name = name + self._clusterrole = None + self._inc_clusterrole = ClusterRole.builder(name, rules) + + @property + def clusterrole(self): + ''' property for clusterrole''' + if not self._clusterrole: + self.get() + return self._clusterrole + + @clusterrole.setter + def clusterrole(self, data): + ''' setter function for clusterrole property''' + self._clusterrole = data + + @property + def inc_clusterrole(self): + ''' property for inc_clusterrole''' + return self._inc_clusterrole + + @inc_clusterrole.setter + def inc_clusterrole(self, data): + ''' setter function for inc_clusterrole property''' + self._inc_clusterrole = data + + def exists(self): + ''' return whether a clusterrole exists ''' + if self.clusterrole: + return True + + return False + + def get(self): + '''return a clusterrole ''' + result = self._get(self.kind, self.name) + + if result['returncode'] == 0: + self.clusterrole = ClusterRole(content=result['results'][0]) + result['results'] = self.clusterrole.yaml_dict + + elif 'clusterrole "{}" not found'.format(self.name) in result['stderr']: + result['returncode'] = 0 + + return result + + def delete(self): + '''delete the object''' + return self._delete(self.kind, self.name) + + def create(self): + '''create a clusterrole from the proposed incoming clusterrole''' + return self._create_from_content(self.name, self.inc_clusterrole.yaml_dict) + + def update(self): + '''update a project''' + return self._replace_content(self.kind, self.name, self.inc_clusterrole.yaml_dict) + + def needs_update(self): + ''' verify an update is needed''' + return not self.clusterrole.compare(self.inc_clusterrole, self.verbose) + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params, check_mode): + '''run the idempotent ansible code''' + + oc_clusterrole = OCClusterRole(params['name'], + params['rules'], + params['kubeconfig'], + params['debug']) + + state = params['state'] + + api_rval = oc_clusterrole.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval, 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_clusterrole.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_clusterrole.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_clusterrole.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_clusterrole.create() + + # return the created object + api_rval = oc_clusterrole.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_clusterrole.needs_update(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'} + + api_rval = oc_clusterrole.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_clusterrole.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, + 'changed': False, + 'msg': 'Unknown state passed. [%s]' % state} diff --git a/roles/lib_openshift/src/class/oc_configmap.py b/roles/lib_openshift/src/class/oc_configmap.py new file mode 100644 index 000000000..de77d1102 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_configmap.py @@ -0,0 +1,191 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-arguments +class OCConfigMap(OpenShiftCLI): + ''' Openshift ConfigMap Class + + ConfigMaps are a way to store data inside of objects + ''' + def __init__(self, + name, + from_file, + from_literal, + state, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCConfigMap, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose) + self.name = name + self.state = state + self._configmap = None + self._inc_configmap = None + self.from_file = from_file if from_file is not None else {} + self.from_literal = from_literal if from_literal is not None else {} + + @property + def configmap(self): + if self._configmap is None: + self._configmap = self.get() + + return self._configmap + + @configmap.setter + def configmap(self, inc_map): + self._configmap = inc_map + + @property + def inc_configmap(self): + if self._inc_configmap is None: + results = self.create(dryrun=True, output=True) + self._inc_configmap = results['results'] + + return self._inc_configmap + + @inc_configmap.setter + def inc_configmap(self, inc_map): + self._inc_configmap = inc_map + + def from_file_to_params(self): + '''return from_files in a string ready for cli''' + return ["--from-file={}={}".format(key, value) for key, value in self.from_file.items()] + + def from_literal_to_params(self): + '''return from_literal in a string ready for cli''' + return ["--from-literal={}={}".format(key, value) for key, value in self.from_literal.items()] + + def get(self): + '''return a configmap by name ''' + results = self._get('configmap', self.name) + if results['returncode'] == 0 and results['results'][0]: + self.configmap = results['results'][0] + + if results['returncode'] != 0 and '"{}" not found'.format(self.name) in results['stderr']: + results['returncode'] = 0 + + return results + + def delete(self): + '''delete a configmap by name''' + return self._delete('configmap', self.name) + + def create(self, dryrun=False, output=False): + '''Create a configmap + + :dryrun: Product what you would have done. default: False + :output: Whether to parse output. default: False + ''' + + cmd = ['create', 'configmap', self.name] + if self.from_literal is not None: + cmd.extend(self.from_literal_to_params()) + + if self.from_file is not None: + cmd.extend(self.from_file_to_params()) + + if dryrun: + cmd.extend(['--dry-run', '-ojson']) + + results = self.openshift_cmd(cmd, output=output) + + return results + + def update(self): + '''run update configmap ''' + return self._replace_content('configmap', self.name, self.inc_configmap) + + def needs_update(self): + '''compare the current configmap with the proposed and return if they are equal''' + return not Utils.check_def_equal(self.inc_configmap, self.configmap, debug=self.verbose) + + @staticmethod + # pylint: disable=too-many-return-statements,too-many-branches + # TODO: This function should be refactored into its individual parts. + def run_ansible(params, check_mode): + '''run the ansible idempotent code''' + + oc_cm = OCConfigMap(params['name'], + params['from_file'], + params['from_literal'], + params['state'], + params['namespace'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = oc_cm.get() + + if 'failed' in api_rval: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval, 'state': state} + + if not params['name']: + return {'failed': True, + 'msg': 'Please specify a name when state is absent|present.'} + + ######## + # 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 = oc_cm.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Create + ######## + if state == 'present': + if not Utils.exists(api_rval['results'], params['name']): + + if check_mode: + return {'changed': True, 'msg': 'Would have performed a create.'} + + api_rval = oc_cm.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + api_rval = oc_cm.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_cm.needs_update(): + + api_rval = oc_cm.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + api_rval = oc_cm.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} diff --git a/roles/lib_openshift/src/class/oc_group.py b/roles/lib_openshift/src/class/oc_group.py new file mode 100644 index 000000000..89fb09ea4 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_group.py @@ -0,0 +1,148 @@ +# pylint: skip-file +# flake8: noqa + + +class OCGroup(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + kind = 'group' + + def __init__(self, + config, + verbose=False): + ''' Constructor for OCGroup ''' + super(OCGroup, self).__init__(config.namespace, config.kubeconfig) + self.config = config + self.namespace = config.namespace + self._group = None + + @property + def group(self): + ''' property function service''' + if not self._group: + self.get() + return self._group + + @group.setter + def group(self, data): + ''' setter function for yedit var ''' + self._group = data + + def exists(self): + ''' return whether a group exists ''' + if self.group: + return True + + return False + + def get(self): + '''return group information ''' + result = self._get(self.kind, self.config.name) + if result['returncode'] == 0: + self.group = Group(content=result['results'][0]) + elif 'groups \"{}\" not found'.format(self.config.name) in result['stderr']: + result['returncode'] = 0 + result['results'] = [{}] + + return result + + def delete(self): + '''delete the object''' + return self._delete(self.kind, self.config.name) + + def create(self): + '''create the object''' + return self._create_from_content(self.config.name, self.config.data) + + def update(self): + '''update the object''' + return self._replace_content(self.kind, self.config.name, self.config.data) + + def needs_update(self): + ''' verify an update is needed ''' + return not Utils.check_def_equal(self.config.data, self.group.yaml_dict, skip_keys=[], debug=True) + + # pylint: disable=too-many-return-statements,too-many-branches + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + + gconfig = GroupConfig(params['name'], + params['namespace'], + params['kubeconfig'], + ) + oc_group = OCGroup(gconfig, verbose=params['debug']) + + state = params['state'] + + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_group.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_group.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_group.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_group.needs_update(): + api_rval = oc_group.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_group.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} diff --git a/roles/lib_openshift/src/class/oc_image.py b/roles/lib_openshift/src/class/oc_image.py new file mode 100644 index 000000000..d25349127 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_image.py @@ -0,0 +1,91 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-arguments +class OCImage(OpenShiftCLI): + ''' Class to import and create an imagestream object''' + def __init__(self, + namespace, + registry_url, + image_name, + image_tag, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OCImage''' + super(OCImage, self).__init__(namespace, kubeconfig) + self.registry_url = registry_url + self.image_name = image_name + self.image_tag = image_tag + self.verbose = verbose + + def get(self): + '''return a image by name ''' + results = self._get('imagestream', self.image_name) + results['exists'] = False + if results['returncode'] == 0 and results['results'][0]: + results['exists'] = True + + if results['returncode'] != 0 and '"{}" not found'.format(self.image_name) in results['stderr']: + results['returncode'] = 0 + + return results + + def create(self, url=None, name=None, tag=None): + '''Create an image ''' + return self._import_image(url, name, tag) + + + # pylint: disable=too-many-return-statements + @staticmethod + def run_ansible(params, check_mode): + ''' run the ansible idempotent code ''' + + ocimage = OCImage(params['namespace'], + params['registry_url'], + params['image_name'], + params['image_tag'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = ocimage.get() + + ##### + # Get + ##### + if state == 'list': + if api_rval['returncode'] != 0: + return {"failed": True, "msg": api_rval} + return {"changed": False, "results": api_rval, "state": "list"} + + ######## + # Create + ######## + if state == 'present': + + if not Utils.exists(api_rval['results'], params['image_name']): + + if check_mode: + return {"changed": False, "msg": 'CHECK_MODE: Would have performed a create'} + + api_rval = ocimage.create(params['registry_url'], + params['image_name'], + params['image_tag']) + + if api_rval['returncode'] != 0: + return {"failed": True, "msg": api_rval} + + # return the newly created object + api_rval = ocimage.get() + + if api_rval['returncode'] != 0: + return {"failed": True, "msg": api_rval} + + return {"changed": True, "results": api_rval, "state": "present"} + + # image exists, no change + return {"changed": False, "results": api_rval, "state": "present"} + + return {"failed": True, "changed": False, "msg": "Unknown state passed. {0}".format(state)} diff --git a/roles/lib_openshift/src/class/oc_label.py b/roles/lib_openshift/src/class/oc_label.py index bd312c170..0a6895177 100644 --- a/roles/lib_openshift/src/class/oc_label.py +++ b/roles/lib_openshift/src/class/oc_label.py @@ -134,9 +134,9 @@ class OCLabel(OpenShiftCLI): label_list = [] if self.name: - result = self._get(resource=self.kind, rname=self.name, selector=self.selector) + result = self._get(resource=self.kind, name=self.name, selector=self.selector) - if 'labels' in result['results'][0]['metadata']: + if result['results'][0] and 'labels' in result['results'][0]['metadata']: label_list.append(result['results'][0]['metadata']['labels']) else: label_list.append({}) diff --git a/roles/lib_openshift/src/class/oc_obj.py b/roles/lib_openshift/src/class/oc_obj.py index 51d3ce996..667b98eac 100644 --- a/roles/lib_openshift/src/class/oc_obj.py +++ b/roles/lib_openshift/src/class/oc_obj.py @@ -10,7 +10,7 @@ class OCObject(OpenShiftCLI): def __init__(self, kind, namespace, - rname=None, + name=None, selector=None, kubeconfig='/etc/origin/master/admin.kubeconfig', verbose=False, @@ -19,21 +19,21 @@ class OCObject(OpenShiftCLI): super(OCObject, self).__init__(namespace, kubeconfig=kubeconfig, verbose=verbose, all_namespaces=all_namespaces) self.kind = kind - self.name = rname + self.name = name self.selector = selector 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 = self._get(self.kind, name=self.name, selector=self.selector) + if (results['returncode'] != 0 and 'stderr' in results and + '\"{}\" not found'.format(self.name) in results['stderr']): results['returncode'] = 0 return results def delete(self): - '''return all pods ''' - return self._delete(self.kind, self.name) + '''delete the object''' + return self._delete(self.kind, name=self.name, selector=self.selector) def create(self, files=None, content=None): ''' @@ -109,24 +109,33 @@ class OCObject(OpenShiftCLI): # 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 + return {'changed': False, 'results': api_rval, 'state': state} ######## # Delete ######## if state == 'absent': - if not Utils.exists(api_rval['results'], params['name']): - return {'changed': False, 'state': 'absent'} + # if we were passed a name, verify its not in our results + if params['name'] is not None and not Utils.exists(api_rval['results'], params['name']): + return {'changed': False, 'state': state} + + # verify results are empty for the selector + if params['selector'] is not None and len(api_rval['results']) == 0: + return {'changed': False, 'state': state} 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 api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + # create/update: Must define a name beyond this point + if not params['name']: + return {'failed': True, 'msg': 'Please specify a name when state is present.'} if state == 'present': ######## @@ -152,7 +161,7 @@ class OCObject(OpenShiftCLI): if params['files'] and params['delete_after']: Utils.cleanup(params['files']) - return {'changed': True, 'results': api_rval, 'state': "present"} + return {'changed': True, 'results': api_rval, 'state': state} ######## # Update @@ -167,7 +176,7 @@ class OCObject(OpenShiftCLI): if params['files'] and params['delete_after']: Utils.cleanup(params['files']) - return {'changed': False, 'results': api_rval['results'][0], 'state': "present"} + return {'changed': False, 'results': api_rval['results'][0], 'state': state} if check_mode: return {'changed': True, 'msg': 'CHECK_MODE: Would have performed an update.'} @@ -186,4 +195,4 @@ class OCObject(OpenShiftCLI): if api_rval['returncode'] != 0: return {'failed': True, 'msg': api_rval} - return {'changed': True, 'results': api_rval, 'state': "present"} + return {'changed': True, 'results': api_rval, 'state': state} diff --git a/roles/lib_openshift/src/class/oc_process.py b/roles/lib_openshift/src/class/oc_process.py index 9d29938aa..eba9a43cd 100644 --- a/roles/lib_openshift/src/class/oc_process.py +++ b/roles/lib_openshift/src/class/oc_process.py @@ -136,7 +136,7 @@ class OCProcess(OpenShiftCLI): if api_rval['returncode'] != 0: return {"failed": True, "msg" : api_rval} - return {"changed" : False, "results": api_rval, "state": "list"} + return {"changed" : False, "results": api_rval, "state": state} elif state == 'present': if check_mode and params['create']: @@ -158,9 +158,9 @@ class OCProcess(OpenShiftCLI): return {"failed": True, "msg": api_rval} if params['create']: - return {"changed": True, "results": api_rval, "state": "present"} + return {"changed": True, "results": api_rval, "state": state} - return {"changed": False, "results": api_rval, "state": "present"} + return {"changed": False, "results": api_rval, "state": state} # verify results update = False @@ -175,11 +175,11 @@ class OCProcess(OpenShiftCLI): update = True if not update: - return {"changed": update, "results": api_rval, "state": "present"} + return {"changed": update, "results": api_rval, "state": state} for cmd in rval: if cmd['returncode'] != 0: - return {"failed": True, "changed": update, "results": rval, "state": "present"} + return {"failed": True, "changed": update, "msg": rval, "state": state} - return {"changed": update, "results": rval, "state": "present"} + return {"changed": update, "results": rval, "state": state} diff --git a/roles/lib_openshift/src/class/oc_project.py b/roles/lib_openshift/src/class/oc_project.py index 7e3984297..9ad8111a8 100644 --- a/roles/lib_openshift/src/class/oc_project.py +++ b/roles/lib_openshift/src/class/oc_project.py @@ -61,30 +61,34 @@ class OCProject(OpenShiftCLI): def update(self): '''update a project ''' - self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) - self.project.update_annotation('description', self.config.config_options['description']['value']) + if self.config.config_options['display_name']['value'] is not None: + self.project.update_annotation('display-name', self.config.config_options['display_name']['value']) + + if self.config.config_options['description']['value'] is not None: + self.project.update_annotation('description', self.config.config_options['description']['value']) # work around for immutable project field - if self.config.config_options['node_selector']['value']: + if self.config.config_options['node_selector']['value'] is not None: self.project.update_annotation('node-selector', self.config.config_options['node_selector']['value']) - else: - self.project.update_annotation('node-selector', self.project.find_annotation('node-selector')) return self._replace_content(self.kind, self.config.name, self.project.yaml_dict) def needs_update(self): ''' verify an update is needed ''' - result = self.project.find_annotation("display-name") - if result != self.config.config_options['display_name']['value']: - return True + if self.config.config_options['display_name']['value'] is not None: + result = self.project.find_annotation("display-name") + if result != self.config.config_options['display_name']['value']: + return True - result = self.project.find_annotation("description") - if result != self.config.config_options['description']['value']: - return True + if self.config.config_options['description']['value'] is not None: + result = self.project.find_annotation("description") + if result != self.config.config_options['description']['value']: + return True - result = self.project.find_annotation("node-selector") - if result != self.config.config_options['node_selector']['value']: - return True + if self.config.config_options['node_selector']['value'] is not None: + result = self.project.find_annotation("node-selector") + if result != self.config.config_options['node_selector']['value']: + return True return False @@ -93,19 +97,22 @@ class OCProject(OpenShiftCLI): def run_ansible(params, check_mode): '''run the idempotent ansible code''' - _ns = None + node_selector = None if params['node_selector'] is not None: - _ns = ','.join(params['node_selector']) - - pconfig = ProjectConfig(params['name'], - 'None', - params['kubeconfig'], - {'admin': {'value': params['admin'], 'include': True}, - 'admin_role': {'value': params['admin_role'], 'include': True}, - 'description': {'value': params['description'], 'include': True}, - 'display_name': {'value': params['display_name'], 'include': True}, - 'node_selector': {'value': _ns, 'include': True}, - }) + node_selector = ','.join(params['node_selector']) + + pconfig = ProjectConfig( + params['name'], + 'None', + params['kubeconfig'], + { + 'admin': {'value': params['admin'], 'include': True}, + 'admin_role': {'value': params['admin_role'], 'include': True}, + 'description': {'value': params['description'], 'include': True}, + 'display_name': {'value': params['display_name'], 'include': True}, + 'node_selector': {'value': node_selector, 'include': True}, + }, + ) oadm_project = OCProject(pconfig, verbose=params['debug']) diff --git a/roles/lib_openshift/src/class/oc_pvc.py b/roles/lib_openshift/src/class/oc_pvc.py new file mode 100644 index 000000000..c73abc47c --- /dev/null +++ b/roles/lib_openshift/src/class/oc_pvc.py @@ -0,0 +1,167 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCPVC(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + kind = 'pvc' + + # pylint allows 5 + # pylint: disable=too-many-arguments + def __init__(self, + config, + verbose=False): + ''' Constructor for OCVolume ''' + super(OCPVC, self).__init__(config.namespace, config.kubeconfig) + self.config = config + self.namespace = config.namespace + self._pvc = None + + @property + def pvc(self): + ''' property function pvc''' + if not self._pvc: + self.get() + return self._pvc + + @pvc.setter + def pvc(self, data): + ''' setter function for yedit var ''' + self._pvc = data + + def bound(self): + '''return whether the pvc is bound''' + if self.pvc.get_volume_name(): + return True + + return False + + def exists(self): + ''' return whether a pvc exists ''' + if self.pvc: + return True + + return False + + def get(self): + '''return pvc information ''' + result = self._get(self.kind, self.config.name) + if result['returncode'] == 0: + self.pvc = PersistentVolumeClaim(content=result['results'][0]) + elif '\"%s\" not found' % self.config.name in result['stderr']: + result['returncode'] = 0 + result['results'] = [{}] + + return result + + def delete(self): + '''delete the object''' + return self._delete(self.kind, self.config.name) + + def create(self): + '''create the object''' + return self._create_from_content(self.config.name, self.config.data) + + def update(self): + '''update the object''' + # need to update the tls information and the service name + return self._replace_content(self.kind, self.config.name, self.config.data) + + def needs_update(self): + ''' verify an update is needed ''' + if self.pvc.get_volume_name() or self.pvc.is_bound(): + return False + + skip = [] + return not Utils.check_def_equal(self.config.data, self.pvc.yaml_dict, skip_keys=skip, debug=True) + + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def run_ansible(params, check_mode): + '''run the idempotent ansible code''' + pconfig = PersistentVolumeClaimConfig(params['name'], + params['namespace'], + params['kubeconfig'], + params['access_modes'], + params['volume_capacity'], + ) + oc_pvc = OCPVC(pconfig, verbose=params['debug']) + + state = params['state'] + + api_rval = oc_pvc.get() + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_pvc.exists(): + + if check_mode: + return {'changed': False, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_pvc.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_pvc.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_pvc.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_pvc.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_pvc.pvc.is_bound() or oc_pvc.pvc.get_volume_name(): + api_rval['msg'] = '##### - This volume is currently bound. Will not update - ####' + return {'changed': False, 'results': api_rval, 'state': state} + + if oc_pvc.needs_update(): + api_rval = oc_pvc.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_pvc.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'results': api_rval, 'state': state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} diff --git a/roles/lib_openshift/src/class/oc_user.py b/roles/lib_openshift/src/class/oc_user.py new file mode 100644 index 000000000..d9e4eac13 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_user.py @@ -0,0 +1,227 @@ +# pylint: skip-file +# flake8: noqa + +# pylint: disable=too-many-instance-attributes +class OCUser(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + kind = 'users' + + def __init__(self, + config, + groups=None, + verbose=False): + ''' Constructor for OCUser ''' + # namespace has no meaning for user operations, hardcode to 'default' + super(OCUser, self).__init__('default', config.kubeconfig) + self.config = config + self.groups = groups + self._user = None + + @property + def user(self): + ''' property function user''' + if not self._user: + self.get() + return self._user + + @user.setter + def user(self, data): + ''' setter function for user ''' + self._user = data + + def exists(self): + ''' return whether a user exists ''' + if self.user: + return True + + return False + + def get(self): + ''' return user information ''' + result = self._get(self.kind, self.config.username) + if result['returncode'] == 0: + self.user = User(content=result['results'][0]) + elif 'users \"%s\" not found' % self.config.username in result['stderr']: + result['returncode'] = 0 + result['results'] = [{}] + + return result + + def delete(self): + ''' delete the object ''' + return self._delete(self.kind, self.config.username) + + def create_group_entries(self): + ''' make entries for user to the provided group list ''' + if self.groups != None: + for group in self.groups: + cmd = ['groups', 'add-users', group, self.config.username] + rval = self.openshift_cmd(cmd, oadm=True) + if rval['returncode'] != 0: + return rval + + return rval + + return {'returncode': 0} + + def create(self): + ''' create the object ''' + rval = self.create_group_entries() + if rval['returncode'] != 0: + return rval + + return self._create_from_content(self.config.username, self.config.data) + + def group_update(self): + ''' update group membership ''' + rval = {'returncode': 0} + cmd = ['get', 'groups', '-o', 'json'] + all_groups = self.openshift_cmd(cmd, output=True) + + # pylint misindentifying all_groups['results']['items'] type + # pylint: disable=invalid-sequence-index + for group in all_groups['results']['items']: + # If we're supposed to be in this group + if group['metadata']['name'] in self.groups \ + and (group['users'] is None or self.config.username not in group['users']): + cmd = ['groups', 'add-users', group['metadata']['name'], + self.config.username] + rval = self.openshift_cmd(cmd, oadm=True) + if rval['returncode'] != 0: + return rval + # else if we're in the group, but aren't supposed to be + elif group['users'] != None and self.config.username in group['users'] \ + and group['metadata']['name'] not in self.groups: + cmd = ['groups', 'remove-users', group['metadata']['name'], + self.config.username] + rval = self.openshift_cmd(cmd, oadm=True) + if rval['returncode'] != 0: + return rval + + return rval + + def update(self): + ''' update the object ''' + rval = self.group_update() + if rval['returncode'] != 0: + return rval + + # need to update the user's info + return self._replace_content(self.kind, self.config.username, self.config.data, force=True) + + def needs_group_update(self): + ''' check if there are group membership changes ''' + cmd = ['get', 'groups', '-o', 'json'] + all_groups = self.openshift_cmd(cmd, output=True) + + # pylint misindentifying all_groups['results']['items'] type + # pylint: disable=invalid-sequence-index + for group in all_groups['results']['items']: + # If we're supposed to be in this group + if group['metadata']['name'] in self.groups \ + and (group['users'] is None or self.config.username not in group['users']): + return True + # else if we're in the group, but aren't supposed to be + elif group['users'] != None and self.config.username in group['users'] \ + and group['metadata']['name'] not in self.groups: + return True + + return False + + def needs_update(self): + ''' verify an update is needed ''' + skip = [] + if self.needs_group_update(): + return True + + return not Utils.check_def_equal(self.config.data, self.user.yaml_dict, skip_keys=skip, debug=True) + + # pylint: disable=too-many-return-statements + @staticmethod + def run_ansible(params, check_mode=False): + ''' run the idempotent ansible code + + params comes from the ansible portion of this module + check_mode: does the module support check mode. (module.check_mode) + ''' + + uconfig = UserConfig(params['kubeconfig'], + params['username'], + params['full_name'], + ) + + oc_user = OCUser(uconfig, params['groups'], + verbose=params['debug']) + state = params['state'] + + api_rval = oc_user.get() + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': "list"} + + ######## + # Delete + ######## + if state == 'absent': + if oc_user.exists(): + + if check_mode: + return {'changed': False, 'msg': 'Would have performed a delete.'} + + api_rval = oc_user.delete() + + return {'changed': True, 'results': api_rval, 'state': "absent"} + return {'changed': False, 'state': "absent"} + + if state == 'present': + ######## + # Create + ######## + if not oc_user.exists(): + + if check_mode: + return {'changed': False, 'msg': 'Would have performed a create.'} + + # Create it here + api_rval = oc_user.create() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_user.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': "present"} + + ######## + # Update + ######## + if oc_user.needs_update(): + api_rval = oc_user.update() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + orig_cmd = api_rval['cmd'] + # return the created object + api_rval = oc_user.get() + # overwrite the get/list cmd + api_rval['cmd'] = orig_cmd + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': "present"} + + return {'changed': False, 'results': api_rval, 'state': "present"} + + return {'failed': True, + 'changed': False, + 'results': 'Unknown state passed. %s' % state, + 'state': "unknown"} diff --git a/roles/lib_openshift/src/class/oc_volume.py b/roles/lib_openshift/src/class/oc_volume.py new file mode 100644 index 000000000..45b58a516 --- /dev/null +++ b/roles/lib_openshift/src/class/oc_volume.py @@ -0,0 +1,195 @@ +# pylint: skip-file +# flake8: noqa + + +# pylint: disable=too-many-instance-attributes +class OCVolume(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + volume_mounts_path = {"pod": "spec.containers[0].volumeMounts", + "dc": "spec.template.spec.containers[0].volumeMounts", + "rc": "spec.template.spec.containers[0].volumeMounts", + } + volumes_path = {"pod": "spec.volumes", + "dc": "spec.template.spec.volumes", + "rc": "spec.template.spec.volumes", + } + + # pylint allows 5 + # pylint: disable=too-many-arguments + def __init__(self, + kind, + resource_name, + namespace, + vol_name, + mount_path, + mount_type, + secret_name, + claim_size, + claim_name, + configmap_name, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OCVolume ''' + super(OCVolume, self).__init__(namespace, kubeconfig) + self.kind = kind + self.volume_info = {'name': vol_name, + 'secret_name': secret_name, + 'path': mount_path, + 'type': mount_type, + 'claimSize': claim_size, + 'claimName': claim_name, + 'configmap_name': configmap_name} + self.volume, self.volume_mount = Volume.create_volume_structure(self.volume_info) + self.name = resource_name + self.namespace = namespace + self.kubeconfig = kubeconfig + self.verbose = verbose + self._resource = None + + @property + def resource(self): + ''' property function for resource var ''' + if not self._resource: + self.get() + return self._resource + + @resource.setter + def resource(self, data): + ''' setter function for resource var ''' + self._resource = data + + def exists(self): + ''' return whether a volume exists ''' + volume_mount_found = False + volume_found = self.resource.exists_volume(self.volume) + if not self.volume_mount and volume_found: + return True + + if self.volume_mount: + volume_mount_found = self.resource.exists_volume_mount(self.volume_mount) + + if volume_found and self.volume_mount and volume_mount_found: + return True + + return False + + def get(self): + '''return volume information ''' + vol = self._get(self.kind, self.name) + if vol['returncode'] == 0: + if self.kind == 'dc': + self.resource = DeploymentConfig(content=vol['results'][0]) + vol['results'] = self.resource.get_volumes() + + return vol + + def delete(self): + '''remove a volume''' + self.resource.delete_volume_by_name(self.volume) + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + def put(self): + '''place volume into dc ''' + self.resource.update_volume(self.volume) + self.resource.get_volumes() + self.resource.update_volume_mount(self.volume_mount) + return self._replace_content(self.kind, self.name, self.resource.yaml_dict) + + def needs_update(self): + ''' verify an update is needed ''' + return self.resource.needs_update_volume(self.volume, self.volume_mount) + + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def run_ansible(params, check_mode=False): + '''run the idempotent ansible code''' + oc_volume = OCVolume(params['kind'], + params['name'], + params['namespace'], + params['vol_name'], + params['mount_path'], + params['mount_type'], + # secrets + params['secret_name'], + # pvc + params['claim_size'], + params['claim_name'], + # configmap + params['configmap_name'], + kubeconfig=params['kubeconfig'], + verbose=params['debug']) + + state = params['state'] + + api_rval = oc_volume.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + ##### + # Get + ##### + if state == 'list': + return {'changed': False, 'results': api_rval['results'], 'state': state} + + ######## + # Delete + ######## + if state == 'absent': + if oc_volume.exists(): + + if check_mode: + return {'changed': False, 'msg': 'CHECK_MODE: Would have performed a delete.'} + + api_rval = oc_volume.delete() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + return {'changed': False, 'state': state} + + if state == 'present': + ######## + # Create + ######## + if not oc_volume.exists(): + + if check_mode: + return {'changed': True, 'msg': 'CHECK_MODE: Would have performed a create.'} + + # Create it here + api_rval = oc_volume.put() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_volume.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, 'state': state} + + ######## + # Update + ######## + if oc_volume.needs_update(): + api_rval = oc_volume.put() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + # return the created object + api_rval = oc_volume.get() + + if api_rval['returncode'] != 0: + return {'failed': True, 'msg': api_rval} + + return {'changed': True, 'results': api_rval, state: state} + + return {'changed': False, 'results': api_rval, state: state} + + return {'failed': True, 'msg': 'Unknown state passed. {}'.format(state)} |