From c00139155579a928dd37655415f4a311e673e305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moser?= Date: Sun, 9 Jun 2019 13:34:25 +0200 Subject: [PATCH] vultr_server: implement support for IDs to match resources (#55762) --- changelogs/fragments/vultr-id_support.yml | 2 + lib/ansible/module_utils/vultr.py | 31 +++++---- .../modules/cloud/vultr/vultr_server.py | 69 +++++++++++-------- .../targets/vultr_server/tasks/main.yml | 36 ++++++++++ 4 files changed, 98 insertions(+), 40 deletions(-) create mode 100644 changelogs/fragments/vultr-id_support.yml diff --git a/changelogs/fragments/vultr-id_support.yml b/changelogs/fragments/vultr-id_support.yml new file mode 100644 index 0000000000..c19df39eba --- /dev/null +++ b/changelogs/fragments/vultr-id_support.yml @@ -0,0 +1,2 @@ +minor_changes: + - vultr_server - Implemented support for using the ID instead of a name to match a resource, especially useful for region, plan and OS type. diff --git a/lib/ansible/module_utils/vultr.py b/lib/ansible/module_utils/vultr.py index 982cf89b3c..5fa903f444 100644 --- a/lib/ansible/module_utils/vultr.py +++ b/lib/ansible/module_utils/vultr.py @@ -205,35 +205,42 @@ class Vultr: except ValueError as e: self.module.fail_json(msg="Could not process response into json: %s" % e) - def query_resource_by_key(self, key, value, resource='regions', query_by='list', params=None, use_cache=False): + def query_resource_by_key(self, key, value, resource='regions', query_by='list', params=None, use_cache=False, id_key=None): if not value: return {} + r_list = None if use_cache: - if resource in self.api_cache: - if self.api_cache[resource] and self.api_cache[resource].get(key) == value: - return self.api_cache[resource] + r_list = self.api_cache.get(resource) - r_list = self.api_query(path="/v1/%s/%s" % (resource, query_by), data=params) + if not r_list: + r_list = self.api_query(path="/v1/%s/%s" % (resource, query_by), data=params) + if use_cache: + self.api_cache.update({ + resource: r_list + }) if not r_list: return {} + elif isinstance(r_list, list): for r_data in r_list: if str(r_data[key]) == str(value): - self.api_cache.update({ - resource: r_data - }) + return r_data + if id_key is not None and to_text(r_data[id_key]) == to_text(value): return r_data elif isinstance(r_list, dict): for r_id, r_data in r_list.items(): if str(r_data[key]) == str(value): - self.api_cache.update({ - resource: r_data - }) + return r_data + if id_key is not None and to_text(r_data[id_key]) == to_text(value): return r_data - self.module.fail_json(msg="Could not find %s with %s: %s" % (resource, key, value)) + if id_key: + msg = "Could not find %s with ID or %s: %s" % (resource, key, value) + else: + msg = "Could not find %s with %s: %s" % (resource, key, value) + self.module.fail_json(msg=msg) @staticmethod def normalize_result(resource, schema, remove_missing_keys=True): diff --git a/lib/ansible/modules/cloud/vultr/vultr_server.py b/lib/ansible/modules/cloud/vultr/vultr_server.py index 42a051b62b..1762ff0c71 100644 --- a/lib/ansible/modules/cloud/vultr/vultr_server.py +++ b/lib/ansible/modules/cloud/vultr/vultr_server.py @@ -29,25 +29,25 @@ options: type: str hostname: description: - - Hostname to assign to this server. + - The hostname to assign to this server. type: str os: description: - - The operating system. + - The operating system name or ID. - Required if the server does not yet exist and is not restoring from a snapshot. type: str snapshot: version_added: "2.8" description: - - Name of snapshot to restore server from. + - Name or ID of the snapshot to restore the server from. type: str firewall_group: description: - - The firewall group to assign this server to. + - The firewall group description or ID to assign this server to. type: str plan: description: - - Plan to use for the server. + - Plan name or ID to use for the server. - Required if the server does not yet exist. type: str force: @@ -55,6 +55,7 @@ options: - Force stop/start the server if required to apply changes - Otherwise a running server will not be changed. type: bool + default: no notify_activate: description: - Whether to send an activation email when the server is ready or not. @@ -82,12 +83,12 @@ options: type: str startup_script: description: - - Name of the startup script to execute on boot. + - Name or ID of the startup script to execute on boot. - Only considered while creating the server. type: str ssh_keys: description: - - List of SSH keys passed to the server on creation. + - List of SSH key names or IDs passed to the server on creation. aliases: [ ssh_key ] type: list reserved_ip_v4: @@ -97,7 +98,7 @@ options: type: str region: description: - - Region the server is deployed into. + - Region name or ID the server is deployed into. - Required if the server does not yet exist. type: str state: @@ -111,6 +112,7 @@ extends_documentation_fragment: vultr EXAMPLES = ''' - name: create server + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" os: CentOS 7 x64 @@ -122,33 +124,39 @@ EXAMPLES = ''' state: present - name: ensure a server is present and started + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" os: CentOS 7 x64 plan: 1024 MB RAM,25 GB SSD,1.00 TB BW + firewall_group: my_group ssh_key: my_key region: Amsterdam state: started -- name: ensure a server is present and stopped +- name: ensure a server is present and stopped provisioned using IDs + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" - os: CentOS 7 x64 - plan: 1024 MB RAM,25 GB SSD,1.00 TB BW - region: Amsterdam + os: "167" + plan: "201" + region: "7" state: stopped - name: ensure an existing server is stopped + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" state: stopped - name: ensure an existing server is started + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" state: started - name: ensure a server is absent + delegate_to: localhost vultr_server: name: "{{ vultr_server_name }}" state: absent @@ -399,7 +407,8 @@ class AnsibleVultrServer(Vultr): key='name', value=os_name, resource='os', - use_cache=True + use_cache=True, + id_key='OSID', ) def get_snapshot(self): @@ -407,7 +416,7 @@ class AnsibleVultrServer(Vultr): key='description', value=self.module.params.get('snapshot'), resource='snapshot', - use_cache=True + id_key='SNAPSHOTID', ) def get_ssh_keys(self): @@ -421,7 +430,8 @@ class AnsibleVultrServer(Vultr): key='name', value=ssh_key_name, resource='sshkey', - use_cache=True + use_cache=True, + id_key='SSHKEYID', ) if ssh_key: ssh_keys.append(ssh_key) @@ -432,7 +442,8 @@ class AnsibleVultrServer(Vultr): key='name', value=self.module.params.get('region'), resource='regions', - use_cache=True + use_cache=True, + id_key='DCID', ) def get_plan(self): @@ -440,7 +451,8 @@ class AnsibleVultrServer(Vultr): key='name', value=self.module.params.get('plan'), resource='plans', - use_cache=True + use_cache=True, + id_key='VPSPLANID', ) def get_firewall_group(self): @@ -448,7 +460,8 @@ class AnsibleVultrServer(Vultr): key='description', value=self.module.params.get('firewall_group'), resource='firewall', - query_by='group_list' + query_by='group_list', + id_key='FIREWALLGROUPID' ) def get_user_data(self): @@ -872,22 +885,22 @@ def main(): argument_spec = vultr_argument_spec() argument_spec.update(dict( name=dict(required=True, aliases=['label']), - hostname=dict(type='str',), - os=dict(type='str',), - snapshot=dict(type='str',), - plan=dict(type='str',), + hostname=dict(type='str'), + os=dict(type='str'), + snapshot=dict(type='str'), + plan=dict(type='str'), force=dict(type='bool', default=False), notify_activate=dict(type='bool', default=False), private_network_enabled=dict(type='bool'), auto_backup_enabled=dict(type='bool'), ipv6_enabled=dict(type='bool'), - tag=dict(type='str',), - reserved_ip_v4=dict(type='str',), - firewall_group=dict(type='str',), - startup_script=dict(type='str',), - user_data=dict(type='str',), + tag=dict(type='str'), + reserved_ip_v4=dict(type='str'), + firewall_group=dict(type='str'), + startup_script=dict(type='str'), + user_data=dict(type='str'), ssh_keys=dict(type='list', aliases=['ssh_key']), - region=dict(type='str',), + region=dict(type='str'), state=dict(choices=['present', 'absent', 'restarted', 'reinstalled', 'started', 'stopped'], default='present'), )) diff --git a/test/integration/targets/vultr_server/tasks/main.yml b/test/integration/targets/vultr_server/tasks/main.yml index a7c41ab837..ac6a6f3f43 100644 --- a/test/integration/targets/vultr_server/tasks/main.yml +++ b/test/integration/targets/vultr_server/tasks/main.yml @@ -37,6 +37,20 @@ - result is failed - 'result.msg == "missing required arguments: os, plan, region"' +- name: test fail if plan does not exist + vultr_server: + name: "{{ vultr_server_name }}" + os: CentOS 6 x64 + plan: does_not_exist + region: Amsterdam + register: result + ignore_errors: yes +- name: verify test fail if plan does not exist + assert: + that: + - result is failed + - 'result.msg == "Could not find plans with ID or name: does_not_exist"' + - name: setup create ssh keys vultr_ssh_key: name: "{{ item.name }}" @@ -374,6 +388,28 @@ - result.vultr_server.auto_backup_enabled == true - result.vultr_server.internal_ip != '' +- name: test update server with IDs idempotence with force + vultr_server: + name: "{{ vultr_server_name }}" + os: "127" + plan: "202" + auto_backup_enabled: yes + private_network_enabled: yes + region: "7" + force: yes + register: result +- name: verify test update server idempotence with force + assert: + that: + - result is not changed + - result.vultr_server.power_status == 'running' + - result.vultr_server.name == vultr_server_name + - result.vultr_server.os == 'CentOS 6 x64' + - result.vultr_server.plan == vultr_server_plan_2 + - result.vultr_server.region == 'Amsterdam' + - result.vultr_server.auto_backup_enabled == true + - result.vultr_server.internal_ip != '' + - name: test update server to stopped in check mode vultr_server: name: "{{ vultr_server_name }}"