From e2d159c40c42946256bc37887322f0e82853e93b Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Fri, 28 Jun 2019 07:59:54 +0530 Subject: [PATCH] VMware: Handle user unauthorization in tagging scenarios (#58405) Handle unauthorization scenarios in VMware tagging APIs. Fixes: #58326 Signed-off-by: Abhijeet Kasurde --- ...58326-vmware_rest_client-handle_unauth.yml | 2 ++ .../module_utils/vmware_rest_client.py | 17 ++++++++++ .../modules/cloud/vmware/vmware_category.py | 19 ++++++++--- .../modules/cloud/vmware/vmware_tag.py | 24 +++++++++++--- .../cloud/vmware/vmware_tag_manager.py | 32 +++++++++++++------ 5 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 changelogs/fragments/58326-vmware_rest_client-handle_unauth.yml diff --git a/changelogs/fragments/58326-vmware_rest_client-handle_unauth.yml b/changelogs/fragments/58326-vmware_rest_client-handle_unauth.yml new file mode 100644 index 00000000000000..ac19e9fc784f32 --- /dev/null +++ b/changelogs/fragments/58326-vmware_rest_client-handle_unauth.yml @@ -0,0 +1,2 @@ +minor_changes: + - Handle user unauthorization errors in VMware REST API code for tagging (https://github.com/ansible/ansible/issues/58326). diff --git a/lib/ansible/module_utils/vmware_rest_client.py b/lib/ansible/module_utils/vmware_rest_client.py index 1e3a9086c5074d..c28fe6919d83f0 100644 --- a/lib/ansible/module_utils/vmware_rest_client.py +++ b/lib/ansible/module_utils/vmware_rest_client.py @@ -29,6 +29,7 @@ try: from com.vmware.vapi.std_client import DynamicID from vmware.vapi.vsphere.client import create_vsphere_client + from com.vmware.vapi.std.errors_client import Unauthorized HAS_VSPHERE = True except ImportError: VSPHERE_IMP_ERR = traceback.format_exc() @@ -49,6 +50,22 @@ def __init__(self, module): self.check_required_library() self.api_client = self.connect_to_vsphere_client() + # Helper function + def get_error_message(self, error): + """ + Helper function to show human readable error messages. + """ + err_msg = [] + if not error.messages: + if isinstance(error, Unauthorized): + return "Authorization required." + return "Generic error occurred." + + for err in error.messages: + err_msg.append(err.default_message % err.args) + + return " ,".join(err_msg) + def check_required_library(self): """ Check required libraries diff --git a/lib/ansible/modules/cloud/vmware/vmware_category.py b/lib/ansible/modules/cloud/vmware/vmware_category.py index 591c622b76b69b..b21c4be9dc3a0f 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_category.py +++ b/lib/ansible/modules/cloud/vmware/vmware_category.py @@ -119,6 +119,7 @@ from ansible.module_utils.vmware_rest_client import VmwareRestClient try: from com.vmware.cis.tagging_client import CategoryModel + from com.vmware.vapi.std.errors_client import Error except ImportError: pass @@ -159,7 +160,11 @@ def state_create_category(self): category_spec.associable_types = set() - category_id = self.category_service.create(category_spec) + try: + category_id = self.category_service.create(category_spec) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + if category_id: self.module.exit_json(changed=True, category_results=dict(msg="Category '%s' created." % category_spec.name, @@ -199,8 +204,11 @@ def state_update_category(self): change_list.append(True) if any(change_list): - self.category_service.update(category_id, category_update_spec) - changed = True + try: + self.category_service.update(category_id, category_update_spec) + changed = True + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) self.module.exit_json(changed=changed, category_results=results) @@ -208,7 +216,10 @@ def state_update_category(self): def state_delete_category(self): """Delete category.""" category_id = self.global_categories[self.category_name]['category_id'] - self.category_service.delete(category_id=category_id) + try: + self.category_service.delete(category_id=category_id) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) self.module.exit_json(changed=True, category_results=dict(msg="Category '%s' deleted." % self.category_name, category_id=category_id)) diff --git a/lib/ansible/modules/cloud/vmware/vmware_tag.py b/lib/ansible/modules/cloud/vmware/vmware_tag.py index 466bd989b7059b..3b031c810d38de 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_tag.py +++ b/lib/ansible/modules/cloud/vmware/vmware_tag.py @@ -107,6 +107,10 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.vmware_rest_client import VmwareRestClient +try: + from com.vmware.vapi.std.errors_client import Error +except ImportError: + pass class VmwareTag(VmwareRestClient): @@ -160,13 +164,18 @@ def state_create_tag(self): self.module.fail_json(msg="Unable to find category specified using 'category_id' - %s" % category_id) tag_spec.category_id = category_id - tag_id = self.tag_service.create(tag_spec) + tag_id = '' + try: + tag_id = self.tag_service.create(tag_spec) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + if tag_id: self.module.exit_json(changed=True, results=dict(msg="Tag '%s' created." % tag_spec.name, tag_id=tag_id)) self.module.exit_json(changed=False, - results=dict(msg="No tag created", tag_id='')) + results=dict(msg="No tag created", tag_id=tag_id)) def state_unchanged(self): """ @@ -189,7 +198,11 @@ def state_update_tag(self): desired_tag_desc = self.params.get('tag_description') if tag_desc != desired_tag_desc: tag_update_spec.description = desired_tag_desc - self.tag_service.update(tag_id, tag_update_spec) + try: + self.tag_service.update(tag_id, tag_update_spec) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + results['msg'] = 'Tag %s updated.' % self.tag_name changed = True @@ -201,7 +214,10 @@ def state_delete_tag(self): """ tag_id = self.global_tags[self.tag_name]['tag_id'] - self.tag_service.delete(tag_id=tag_id) + try: + self.tag_service.delete(tag_id=tag_id) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) self.module.exit_json(changed=True, results=dict(msg="Tag '%s' deleted." % self.tag_name, tag_id=tag_id)) diff --git a/lib/ansible/modules/cloud/vmware/vmware_tag_manager.py b/lib/ansible/modules/cloud/vmware/vmware_tag_manager.py index eeef79383739ce..0fcaeb8fb6cec9 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_tag_manager.py +++ b/lib/ansible/modules/cloud/vmware/vmware_tag_manager.py @@ -137,6 +137,7 @@ from ansible.module_utils.vmware import (PyVmomi, find_dvs_by_name, find_dvspg_by_name) try: from com.vmware.vapi.std_client import DynamicID + from com.vmware.vapi.std.errors_client import Error except ImportError: pass @@ -239,20 +240,31 @@ def ensure_state(self): if action in ('add', 'present'): if tag_obj not in available_tag_obj: # Tag is not already applied - self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) - changed = True + try: + self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) + changed = True + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + elif action == 'set': # Remove all tags first - if not removed_tags_for_set: - for av_tag in available_tag_obj: - self.tag_association_svc.detach(tag_id=av_tag.id, object_id=self.dynamic_managed_object) - removed_tags_for_set = True - self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) - changed = True + try: + if not removed_tags_for_set: + for av_tag in available_tag_obj: + self.tag_association_svc.detach(tag_id=av_tag.id, object_id=self.dynamic_managed_object) + removed_tags_for_set = True + self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) + changed = True + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + elif action in ('remove', 'absent'): if tag_obj in available_tag_obj: - self.tag_association_svc.detach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) - changed = True + try: + self.tag_association_svc.detach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object) + changed = True + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) results['tag_status']['current_tags'] = [tag.name for tag in self.get_tags_for_object(self.tag_service, self.tag_association_svc,