summaryrefslogtreecommitdiffstats
path: root/ansible_collections/vultr/cloud/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
commit7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch)
treeefb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/vultr/cloud/plugins
parentReleasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff)
downloadansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz
ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/vultr/cloud/plugins')
-rw-r--r--ansible_collections/vultr/cloud/plugins/inventory/vultr.py12
-rw-r--r--ansible_collections/vultr/cloud/plugins/module_utils/common_instance.py247
-rw-r--r--ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py45
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/bare_metal.py440
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py15
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/instance.py218
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/instance_info.py4
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/object_storage.py197
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py3
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/vpc2.py191
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/vpc2_info.py122
11 files changed, 1285 insertions, 209 deletions
diff --git a/ansible_collections/vultr/cloud/plugins/inventory/vultr.py b/ansible_collections/vultr/cloud/plugins/inventory/vultr.py
index c08446134..060813af5 100644
--- a/ansible_collections/vultr/cloud/plugins/inventory/vultr.py
+++ b/ansible_collections/vultr/cloud/plugins/inventory/vultr.py
@@ -66,6 +66,7 @@ options:
description:
- Instance attributes to add as host variables to each host added to inventory.
- See U(https://www.vultr.com/api/#operation/list-instances) for valid values.
+ - The I(internal_ip) attribute was added in version 1.10.0.
type: list
elements: str
default:
@@ -77,6 +78,7 @@ options:
- main_ip
- v6_main_ip
- tags
+ - internal_ip
filters:
description:
- Filter hosts with Jinja2 templates.
@@ -116,7 +118,7 @@ notes:
EXAMPLES = """
---
-# File endings vultr{,-{hosts,instances}}.y{,a}ml
+# File endings vultr{,_{hosts,instances}}.y{,a}ml
# All configuration done via environment variables:
plugin: vultr.cloud.vultr
@@ -147,6 +149,11 @@ plugin: vultr.cloud.vultr
compose:
ansible_host: vultr_v6_main_ip or vultr_main_ip
+# Use the internal IP
+plugin: vultr.cloud.vultr
+compose:
+ ansible_host: vultr_internal_ip
+
# Querying the bare metal instances
plugin: vultr.cloud.vultr
instance_type: bare_metal
@@ -160,7 +167,8 @@ from ansible.errors import AnsibleError, AnsibleParserError
from ansible.module_utils._text import to_native
from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
from ansible.module_utils.urls import Request
-from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
+from ansible.plugins.inventory import (BaseInventoryPlugin, Cacheable,
+ Constructable)
from ..module_utils.vultr_v2 import VULTR_USER_AGENT
diff --git a/ansible_collections/vultr/cloud/plugins/module_utils/common_instance.py b/ansible_collections/vultr/cloud/plugins/module_utils/common_instance.py
new file mode 100644
index 000000000..8e6bcac56
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/module_utils/common_instance.py
@@ -0,0 +1,247 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import base64
+
+from .vultr_v2 import AnsibleVultr
+
+
+class AnsibleVultrCommonInstance(AnsibleVultr):
+ VPC_CONFIGS = {
+ "v1": {
+ "param": "vpcs",
+ "path": "/vpcs",
+ "suffix": "",
+ },
+ "v2": {
+ "param": "vpc2s",
+ "path": "/vpc2",
+ "suffix": "2",
+ },
+ }
+
+ def get_ssh_key_ids(self):
+ ssh_key_names = list(self.module.params["ssh_keys"])
+ ssh_keys = self.query_list(path="/ssh-keys", result_key="ssh_keys")
+
+ ssh_key_ids = list()
+ for ssh_key in ssh_keys:
+ if ssh_key["name"] in ssh_key_names:
+ ssh_key_ids.append(ssh_key["id"])
+ ssh_key_names.remove(ssh_key["name"])
+
+ if ssh_key_names:
+ self.module.fail_json(msg="SSH key names not found: %s" % ", ".join(ssh_key_names))
+
+ return ssh_key_ids
+
+ def get_resource_vpcs(self, resource, api_version="v1"):
+ path = "%s/%s" % (self.resource_path, resource["id"] + self.VPC_CONFIGS[api_version]["path"])
+ vpcs = self.query_list(path=path, result_key="vpcs")
+
+ # TODO: Workaround to get the description field into the list if missing
+ result = list()
+ for vpc in vpcs:
+ if "description" in vpc:
+ return vpcs
+
+ vpc_detail = self.query_by_id(resource_id=vpc["id"], path=self.VPC_CONFIGS[api_version]["path"], result_key="vpc")
+ vpc["description"] = vpc_detail["description"]
+ result.append(vpc)
+ return result
+
+ def get_vpc_ids(self, api_version="v1"):
+ vpc_names = list(self.module.params[self.VPC_CONFIGS[api_version]["param"]])
+ vpcs = self.query_list(self.VPC_CONFIGS[api_version]["path"], result_key="vpcs")
+
+ vpc_ids = list()
+ for vpc in vpcs:
+ if self.module.params["region"] != vpc["region"]:
+ continue
+
+ if vpc["description"] in vpc_names:
+ vpc_ids.append(vpc["id"])
+ vpc_names.remove(vpc["description"])
+
+ if vpc_names:
+ self.module.fail_json(msg="VPCs (%s) not found: %s" % (api_version, ", ".join(vpc_names)))
+
+ return vpc_ids
+
+ def get_firewall_group(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="firewall_group",
+ path="/firewalls",
+ result_key="firewall_groups",
+ fail_not_found=True,
+ )
+
+ def get_snapshot(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="snapshot",
+ path="/snapshots",
+ result_key="snapshots",
+ fail_not_found=True,
+ )
+
+ def get_startup_script(self):
+ return self.query_filter_list_by_name(
+ key_name="name",
+ param_key="startup_script",
+ path="/startup-scripts",
+ result_key="startup_scripts",
+ fail_not_found=True,
+ )
+
+ def get_os(self):
+ return self.query_filter_list_by_name(
+ key_name="name",
+ param_key="os",
+ path="/os",
+ result_key="os",
+ fail_not_found=True,
+ )
+
+ def get_app(self):
+ return self.query_filter_list_by_name(
+ key_name="deploy_name",
+ param_key="app",
+ path="/applications",
+ result_key="applications",
+ fail_not_found=True,
+ query_params={"type": "one-click"},
+ )
+
+ def get_image(self):
+ return self.query_filter_list_by_name(
+ key_name="deploy_name",
+ param_key="image",
+ path="/applications",
+ result_key="applications",
+ fail_not_found=True,
+ query_params={"type": "marketplace"},
+ )
+
+ def get_user_data(self, resource):
+ res = self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], "user-data"),
+ )
+ if res:
+ return str(res.get("user_data", dict()).get("data"))
+ return ""
+
+ def transform_resource(self, resource):
+ if not resource:
+ return resource
+
+ features = resource.get("features", list())
+ # Cloud instance features
+ if "backups" in self.module.params:
+ resource["backups"] = "enabled" if "auto_backups" in features else "disabled"
+ if "ddos_protection" in self.module.params:
+ resource["ddos_protection"] = "ddos_protection" in features
+
+ # Bare metal features
+ if "persistent_pxe" in self.module.params:
+ resource["persistent_pxe"] = "persistent_pxe" in features
+
+ # Common features
+ resource["enable_ipv6"] = "ipv6" in features
+
+ # VPCs
+ if "vpcs" in self.module.params:
+ resource["vpcs"] = self.get_resource_vpcs(resource=resource)
+ if "vpc2s" in self.module.params:
+ resource["vpc2s"] = self.get_resource_vpcs(resource=resource, api_version="v2")
+
+ return resource
+
+ def get_detach_vpcs_ids(self, resource, api_version="v1"):
+ detach_vpc_ids = []
+ for vpc in resource.get(self.VPC_CONFIGS[api_version]["param"], list()):
+ param = "attach_vpc%s" % self.VPC_CONFIGS[api_version]["suffix"]
+ if vpc["id"] not in list(self.module.params[param]):
+ detach_vpc_ids.append(vpc["id"])
+ return detach_vpc_ids
+
+ def configure(self):
+ if self.module.params["state"] != "absent":
+ if self.module.params.get("startup_script") is not None:
+ self.module.params["script_id"] = self.get_startup_script()["id"]
+
+ if self.module.params.get("snapshot") is not None:
+ self.module.params["snapshot_id"] = self.get_snapshot()["id"]
+
+ if self.module.params.get("os") is not None:
+ self.module.params["os_id"] = self.get_os()["id"]
+
+ if self.module.params.get("app") is not None:
+ self.module.params["app_id"] = self.get_app()["id"]
+
+ if self.module.params.get("image") is not None:
+ self.module.params["image_id"] = self.get_image()["image_id"]
+
+ if self.module.params.get("user_data") is not None:
+ self.module.params["user_data"] = base64.b64encode(self.module.params["user_data"].encode())
+
+ if self.module.params.get("ssh_keys") is not None:
+ # sshkey_id ist a list of ids
+ self.module.params["sshkey_id"] = self.get_ssh_key_ids()
+
+ if self.module.params.get("vpcs") is not None:
+ # attach_vpc is a list of ids used while creating
+ self.module.params["attach_vpc"] = self.get_vpc_ids()
+
+ if self.module.params.get("vpc2s") is not None:
+ # attach_vpc2 is a list of ids used while creating
+ self.module.params["attach_vpc2"] = self.get_vpc_ids(api_version="v2")
+
+ def create(self):
+ param_keys = ("os", "image", "app", "snapshot")
+ if not any(self.module.params.get(x) is not None for x in param_keys):
+ self.module.fail_json(msg="missing required arguements, one of the following required: %s" % ", ".join(param_keys))
+ return super(AnsibleVultrCommonInstance, self).create()
+
+ def update(self, resource):
+ user_data = self.get_user_data(resource=resource)
+ resource["user_data"] = user_data.encode()
+
+ # VPC1
+ if self.module.params.get("vpcs") is not None:
+ resource["attach_vpc"] = list()
+ for vpc in list(resource["vpcs"]):
+ resource["attach_vpc"].append(vpc["id"])
+
+ # detach_vpc is a list of ids to be detached
+ resource["detach_vpc"] = list()
+ self.module.params["detach_vpc"] = self.get_detach_vpcs_ids(resource=resource)
+
+ # VPC2
+ if self.module.params.get("vpc2s") is not None:
+ resource["attach_vpc2"] = list()
+ for vpc in list(resource["vpc2s"]):
+ resource["attach_vpc2"].append(vpc["id"])
+
+ # detach_vpc2 is a list of ids to be detached
+ resource["detach_vpc2"] = list()
+ self.module.params["detach_vpc2"] = self.get_detach_vpcs_ids(resource=resource, api_version="v2")
+
+ return super(AnsibleVultrCommonInstance, self).update(resource=resource)
+
+ def create_or_update(self):
+ resource = super(AnsibleVultrCommonInstance, self).create_or_update()
+ if resource:
+ resource = self.wait_for_state(resource=resource, key="status", states=["active"], retries=300)
+ return resource
+
+ def transform_result(self, resource):
+ if resource:
+ resource["user_data"] = self.get_user_data(resource=resource)
+ return resource
diff --git a/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py b/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py
index fb49d6180..602e89605 100644
--- a/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py
+++ b/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py
@@ -75,7 +75,6 @@ class AnsibleVultr:
resource_update_param_keys=None,
resource_update_method="PATCH",
):
-
self.module = module
self.namespace = namespace
@@ -165,7 +164,8 @@ class AnsibleVultr:
# Check for:
# 429 Too Many Requests
# 500 Internal Server Error
- if info["status"] not in (429, 500):
+ # 504 Gateway Time-out
+ if info["status"] not in (429, 500, 504):
break
# Vultr has a rate limiting requests per second, try to be polite
@@ -202,9 +202,23 @@ class AnsibleVultr:
found = dict()
for resource in self.query_list(path=path, result_key=result_key, query_params=query_params):
if resource.get(key_name) == param_value:
+ # In case the resource has a region, distinguish between the region
+ # This allows to have identical identifiers (e.g. names) per region
+ region_param = self.module.params.get("region")
+ region_resource = resource.get("region")
+ if region_resource and region_param and (region_param != region_resource):
+ continue
+
if found:
- self.module.fail_json(msg="More than one record with name=%s found. " "Use multiple=true if module supports it." % param_value)
+ if region_resource and not region_param:
+ msg = "More than one record with name=%s found. Use region to distinguish." % param_value
+ else:
+ msg = "More than one record with name=%s found. Use multiple=true if module supports it." % param_value
+
+ self.module.fail_json(msg=msg)
+
found = resource
+
if found:
if get_details:
return self.query_by_id(resource_id=found[key_id], skip_transform=skip_transform)
@@ -256,21 +270,24 @@ class AnsibleVultr:
resources = self.api_query(path=path, query_params=query_params)
return resources[result_key] if resources else []
- def wait_for_state(self, resource, key, states, cmp="="):
- for retry in range(0, 60):
- resource = self.query_by_id(resource_id=resource[self.resource_key_id], skip_transform=False)
- if cmp == "=":
- if key not in resource or resource[key] in states or not resource[key]:
- break
- else:
- if key not in resource or resource[key] not in states or not resource[key]:
- break
+ def wait_for_state(self, resource, key, states, cmp="=", retries=60):
+ resource_id = resource[self.resource_key_id]
+ for retry in range(0, retries):
+ resource = self.query_by_id(resource_id=resource_id, skip_transform=False)
+ if resource and key in resource:
+ if cmp == "=":
+ if resource[key] in states:
+ break
+ else:
+ if resource[key] not in states:
+ break
backoff(retry=retry)
else:
if cmp == "=":
- self.module.fail_json(msg="Wait for %s to become %s timed out" % (key, states))
+ msg = "Wait for %s to become one in %s timed out" % (key, states)
else:
- self.module.fail_json(msg="Wait for %s to not be in %s timed out" % (key, states))
+ msg = "Wait for %s to not be in %s timed out" % (key, states)
+ self.module.fail_json(msg=msg)
return resource
diff --git a/ansible_collections/vultr/cloud/plugins/modules/bare_metal.py b/ansible_collections/vultr/cloud/plugins/modules/bare_metal.py
new file mode 100644
index 000000000..d2ffe09ef
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/bare_metal.py
@@ -0,0 +1,440 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+---
+module: bare_metal
+short_description: Manages bare metal machines on Vultr.
+description:
+ - Manage bare metal machines on Vultr.
+version_added: "1.9.0"
+author:
+ - "René Moser (@resmo)"
+options:
+ label:
+ description:
+ - Name of the bare metal machine.
+ required: true
+ aliases: [ name ]
+ type: str
+ hostname:
+ description:
+ - The hostname to assign to this bare metal machine.
+ type: str
+ os:
+ description:
+ - The operating system name.
+ - Mutually exclusive with I(image) and I(app).
+ type: str
+ app:
+ description:
+ - The app deploy name of Vultr OneClick apps.
+ - Mutually exclusive with I(image) and I(os).
+ type: str
+ image:
+ description:
+ - The image deploy name of Vultr Marketplace apps.
+ - Mutually exclusive with I(os) and I(app).
+ type: str
+ plan:
+ description:
+ - The plan name to use for the bare metal machine.
+ - Required if the bare metal machine does not yet exist.
+ type: str
+ activation_email:
+ description:
+ - Whether to send an activation email when the bare metal machine is ready or not.
+ - Only considered on creation.
+ type: bool
+ default: false
+ persistent_pxe:
+ description:
+ - Whether to enable persistent PXE or not.
+ type: bool
+ enable_ipv6:
+ description:
+ - Whether to enable IPv6 or not.
+ type: bool
+ tags:
+ description:
+ - Tags for the bare metal machine.
+ type: list
+ elements: str
+ user_data:
+ description:
+ - User data to be passed to the bare metal machine.
+ type: str
+ startup_script:
+ description:
+ - Name or ID of the startup script to execute on boot.
+ - Only considered while creating the bare metal machine.
+ type: str
+ ssh_keys:
+ description:
+ - List of SSH key names passed to the bare metal machine on creation.
+ type: list
+ elements: str
+ snapshot:
+ description:
+ - Description or ID of the snapshot.
+ - Only considered while creating the bare metal machine.
+ type: str
+ reserved_ipv4:
+ description:
+ - IP address of the floating IP to use as the main IP of this bare metal machine.
+ - Only considered on creation.
+ type: str
+ region:
+ description:
+ - Region the bare metal machine is deployed into.
+ type: str
+ required: true
+ vpc2s:
+ description:
+ - A list of VPCs (VPC 2.0) identified by their description to be assigned to the bare metal machine.
+ type: list
+ elements: str
+ state:
+ description:
+ - State of the bare metal machine.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+---
+- name: Create an bare metal machine using OS
+ vultr.cloud.bare_metal:
+ label: my web server
+ hostname: my-hostname
+ user_data: |
+ #cloud-config
+ packages:
+ - nginx
+ plan: vbm-4c-32gb
+ enable_ipv6: true
+ ssh_keys:
+ - my ssh key
+ vpc2s:
+ - my vpc description
+ tags:
+ - web
+ - project-genesis
+ region: ams
+ os: Debian 12 x64 (bookworm)
+
+- name: Deploy an bare metal machine of a marketplace app
+ vultr.cloud.bare_metal:
+ label: git-server
+ hostname: git
+ plan: vbm-4c-32gb
+ enable_ipv6: true
+ region: ams
+ image: Gitea on Ubuntu 20.04
+
+- name: Delete an bare metal machine
+ vultr.cloud.bare_metal:
+ label: my web server
+ region: ams
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_bare_metal:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the bare metal machine.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ v6_main_ip:
+ description: IPv6 of the bare metal machine.
+ returned: success
+ type: str
+ sample: ""
+ v6_network:
+ description: IPv6 network of the bare metal machine.
+ returned: success
+ type: str
+ sample: ""
+ v6_network_size:
+ description: IPv6 network size of the bare metal machine.
+ returned: success
+ type: int
+ sample: 0
+ mac_address:
+ description: MAC address of the bare metal machine.
+ returned: success
+ type: int
+ sample: 2199756823533
+ main_ip:
+ description: IPv4 of the bare metal machine.
+ returned: success
+ type: str
+ sample: 95.179.189.95
+ netmask_v4:
+ description: Netmask IPv4 of the bare metal machine.
+ returned: success
+ type: str
+ sample: 255.255.254.0
+ gateway_v4:
+ description: Gateway IPv4.
+ returned: success
+ type: str
+ sample: 95.179.188.1
+ disk:
+ description: Disk info of the bare metal machine.
+ returned: success
+ type: str
+ sample: "2x 240GB SSD"
+ cpu_count:
+ description: CPU count of the bare metal machine.
+ returned: success
+ type: int
+ sample: 1
+ plan:
+ description: Plan of the bare metal machine.
+ returned: success
+ type: str
+ sample: vbm-4c-32gb
+ image_id:
+ description: Image ID of the bare metal machine.
+ returned: success
+ type: str
+ sample: ""
+ os_id:
+ description: OS ID of the bare metal machine.
+ returned: success
+ type: int
+ sample: 186
+ app_id:
+ description: App ID of the bare metal machine.
+ returned: success
+ type: int
+ sample: 37
+ date_created:
+ description: Date when the bare metal machine was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ label:
+ description: Label of the bare metal machine.
+ returned: success
+ type: str
+ sample: my bare metal machine
+ region:
+ description: Region the bare metal machine was deployed into.
+ returned: success
+ type: str
+ sample: ews
+ status:
+ description: Status about the deployment of the bare metal machine.
+ returned: success
+ type: str
+ sample: active
+ default_password:
+ description: The default password assigned at deployment. Only available for ten minutes after deployment.
+ returned: success
+ type: str
+ sample: "examplePassword"
+ power_status:
+ description: Power status of the bare metal machine.
+ returned: success
+ type: str
+ sample: running
+ ram:
+ description: RAM info of the bare metal machine.
+ returned: success
+ type: str
+ sample: "32768 MB"
+ os:
+ description: OS of the bare metal machine.
+ returned: success
+ type: str
+ sample: Application
+ tags:
+ description: Tags of the bare metal machine.
+ returned: success
+ type: list
+ sample: [ my-tag ]
+ features:
+ description: Features of the bare metal machine.
+ returned: success
+ type: list
+ sample: [ ddos_protection, ipv6, auto_backups ]
+ user_data:
+ description: Base64 encoded user data (cloud init) of the bare metal machine.
+ returned: success
+ type: str
+ sample: I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIGh0b3AK
+ enable_ipv6:
+ description: Whether IPv6 is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ vpc2s:
+ description: List of VPCs (VPC 2.0) attached.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: 5536d2a4-66fd-4dfb-b839-7672fd5bc116
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: my vpc
+ region:
+ description: Region the VPC is assigned to.
+ returned: success
+ type: str
+ sample: ews
+ date_created:
+ description: Date when the VPC was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ ip_block:
+ description: IP block assigned to the VPC.
+ returned: success
+ type: str
+ sample: "10.99.0.0"
+ prefix_length:
+ description: The number of bits for the netmask in CIDR notation.
+ returned: success
+ type: int
+ sample: 24
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.common_instance import AnsibleVultrCommonInstance
+from ..module_utils.vultr_v2 import vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ label=dict(type="str", required=True, aliases=["name"]),
+ hostname=dict(type="str"),
+ app=dict(type="str"),
+ image=dict(type="str"),
+ snapshot=dict(type="str"),
+ os=dict(type="str"),
+ plan=dict(type="str"),
+ activation_email=dict(type="bool", default=False),
+ enable_ipv6=dict(type="bool"),
+ persistent_pxe=dict(type="bool"),
+ tags=dict(type="list", elements="str"),
+ vpc2s=dict(type="list", elements="str"),
+ reserved_ipv4=dict(type="str"),
+ startup_script=dict(type="str"),
+ user_data=dict(type="str"),
+ ssh_keys=dict(type="list", elements="str", no_log=False),
+ region=dict(type="str", required=True),
+ state=dict(
+ choices=[
+ "present",
+ "absent",
+ ],
+ default="present",
+ ),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=(("state", "present", ("plan",)),),
+ mutually_exclusive=(("os", "app", "image", "snapshot"),),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrCommonInstance(
+ module=module,
+ namespace="vultr_bare_metal",
+ resource_path="/bare-metals",
+ ressource_result_key_singular="bare_metal",
+ resource_create_param_keys=[
+ "label",
+ "hostname",
+ "plan",
+ "app_id",
+ "os_id",
+ "iso_id",
+ "image_id",
+ "snapshot_id",
+ "script_id",
+ "region",
+ "enable_ipv6",
+ "reserved_ipv4",
+ "user_data",
+ "tags",
+ "activation_email",
+ "sshkey_id",
+ "persistent_pxe",
+ "attach_vpc2",
+ ],
+ resource_update_param_keys=[
+ "plan",
+ "tags",
+ "enable_ipv6",
+ "user_data",
+ "attach_vpc2",
+ "detach_vpc2",
+ ],
+ resource_key_name="label",
+ )
+
+ state = module.params.get("state") # type: ignore
+ if state == "absent":
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py
index 474372d08..67dd85f4d 100644
--- a/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py
+++ b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py
@@ -212,6 +212,21 @@ class AnsibleVultrFirewallRule(AnsibleVultr):
if source is not None and source != "cloudflare":
self.module.params["source"] = self.get_load_balancer()["id"]
+ # Warn about port only affects TCP and UDP protocol
+ if (
+ self.module.params.get("protocol")
+ not in (
+ "tcp",
+ "udp",
+ )
+ and self.module.params.get("port") is not None
+ ):
+ self.module.warn(
+ "Setting a port (%s) only affects protocols TCP/UDP, but protocol is: %s. Ignoring."
+ % (self.module.params.get("port"), self.module.params.get("protocol"))
+ )
+ self.module.params["port"] = None
+
def query(self):
result = dict()
for resource in self.query_list():
diff --git a/ansible_collections/vultr/cloud/plugins/modules/instance.py b/ansible_collections/vultr/cloud/plugins/modules/instance.py
index 73099164d..7eca359b4 100644
--- a/ansible_collections/vultr/cloud/plugins/modules/instance.py
+++ b/ansible_collections/vultr/cloud/plugins/modules/instance.py
@@ -80,6 +80,14 @@ options:
description:
- User data to be passed to the instance.
type: str
+ user_scheme:
+ description:
+ - The user scheme used as login user (Linux-only).
+ - By default, the I(root) user is configured.
+ - Only considered while creating the instance.
+ type: str
+ choices: [ root, limited ]
+ version_added: "1.11.0"
startup_script:
description:
- Name or ID of the startup script to execute on boot.
@@ -146,7 +154,7 @@ EXAMPLES = """
- web
- project-genesis
region: ams
- os: Debian 11 x64 (bullseye)
+ os: Debian 12 x64 (bookworm)
- name: Deploy an instance of a marketplace app
vultr.cloud.instance:
@@ -362,6 +370,12 @@ vultr_instance:
returned: success
type: str
sample: I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIGh0b3AK
+ user_scheme:
+ description: The user scheme to login into this instance
+ returned: success
+ type: str
+ sample: root
+ version_added: "1.11.0"
backups:
description: Whether backups are enabled or disabled.
returned: success
@@ -408,173 +422,14 @@ vultr_instance:
sample: "5a:01:04:3d:5e:72"
"""
-import base64
from ansible.module_utils.basic import AnsibleModule
-from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
-
-
-class AnsibleVultrInstance(AnsibleVultr):
- def get_ssh_key_ids(self):
- ssh_key_names = list(self.module.params["ssh_keys"])
- ssh_keys = self.query_list(path="/ssh-keys", result_key="ssh_keys")
-
- ssh_key_ids = list()
- for ssh_key in ssh_keys:
- if ssh_key["name"] in ssh_key_names:
- ssh_key_ids.append(ssh_key["id"])
- ssh_key_names.remove(ssh_key["name"])
-
- if ssh_key_names:
- self.module.fail_json(msg="SSH key names not found: %s" % ", ".join(ssh_key_names))
-
- return ssh_key_ids
-
- def get_vpc_ids(self):
- vpc_names = list(self.module.params["vpcs"])
- vpcs = self.query_list(path="/vpcs", result_key="vpcs")
-
- vpc_ids = list()
- for vpc in vpcs:
- if vpc["description"] in vpc_names:
- vpc_ids.append(vpc["id"])
- vpc_names.remove(vpc["description"])
-
- if vpc_names:
- self.module.fail_json(msg="VPCs not found: %s" % ", ".join(vpc_names))
-
- return vpc_ids
-
- def get_instance_vpcs(self, resource):
- path = "/instances/%s/vpcs" % resource["id"]
- vpcs = self.query_list(path=path, result_key="vpcs")
-
- # Workaround to get the description field into the list
- result = list()
- for vpc in vpcs:
- vpc_detail = self.query_by_id(resource_id=vpc["id"], path="/vpcs", result_key="vpc")
- vpc["description"] = vpc_detail["description"]
- result.append(vpc)
- return result
-
- def get_firewall_group(self):
- return self.query_filter_list_by_name(
- key_name="description",
- param_key="firewall_group",
- path="/firewalls",
- result_key="firewall_groups",
- fail_not_found=True,
- )
-
- def get_snapshot(self):
- return self.query_filter_list_by_name(
- key_name="description",
- param_key="snapshot",
- path="/snapshots",
- result_key="snapshots",
- fail_not_found=True,
- )
-
- def get_startup_script(self):
- return self.query_filter_list_by_name(
- key_name="name",
- param_key="startup_script",
- path="/startup-scripts",
- result_key="startup_scripts",
- fail_not_found=True,
- )
-
- def get_os(self):
- return self.query_filter_list_by_name(
- key_name="name",
- param_key="os",
- path="/os",
- result_key="os",
- fail_not_found=True,
- )
-
- def get_app(self):
- return self.query_filter_list_by_name(
- key_name="deploy_name",
- param_key="app",
- path="/applications",
- result_key="applications",
- fail_not_found=True,
- query_params={"type": "one-click"},
- )
-
- def get_image(self):
- return self.query_filter_list_by_name(
- key_name="deploy_name",
- param_key="image",
- path="/applications",
- result_key="applications",
- fail_not_found=True,
- query_params={"type": "marketplace"},
- )
-
- def get_user_data(self, resource):
- res = self.api_query(
- path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], "user-data"),
- )
- if res:
- return str(res.get("user_data", dict()).get("data"))
- return ""
-
- def transform_resource(self, resource):
- if not resource:
- return resource
-
- features = resource.get("features", list())
- resource["backups"] = "enabled" if "auto_backups" in features else "disabled"
- resource["enable_ipv6"] = "ipv6" in features
- resource["ddos_protection"] = "ddos_protection" in features
- resource["vpcs"] = self.get_instance_vpcs(resource=resource)
-
- return resource
-
- def get_detach_vpcs_ids(self, resource):
- detach_vpc_ids = []
- for vpc in resource.get("vpcs", list()):
- if vpc["id"] not in list(self.module.params["attach_vpc"]):
- detach_vpc_ids.append(vpc["id"])
- return detach_vpc_ids
-
- def configure(self):
- if self.module.params["state"] != "absent":
- if self.module.params["startup_script"] is not None:
- self.module.params["script_id"] = self.get_startup_script()["id"]
-
- if self.module.params["snapshot"] is not None:
- self.module.params["snapshot_id"] = self.get_snapshot()["id"]
-
- if self.module.params["firewall_group"] is not None:
- self.module.params["firewall_group_id"] = self.get_firewall_group()["id"]
-
- if self.module.params["os"] is not None:
- self.module.params["os_id"] = self.get_os()["id"]
-
- if self.module.params["app"] is not None:
- self.module.params["app_id"] = self.get_app()["id"]
-
- if self.module.params["image"] is not None:
- self.module.params["image_id"] = self.get_image()["image_id"]
-
- if self.module.params["user_data"] is not None:
- self.module.params["user_data"] = base64.b64encode(self.module.params["user_data"].encode())
-
- if self.module.params["ssh_keys"] is not None:
- # sshkey_id ist a list of ids
- self.module.params["sshkey_id"] = self.get_ssh_key_ids()
-
- if self.module.params["backups"] is not None:
- self.module.params["backups"] = "enabled" if self.module.params["backups"] else "disabled"
+from ..module_utils.common_instance import AnsibleVultrCommonInstance
+from ..module_utils.vultr_v2 import vultr_argument_spec
- if self.module.params["vpcs"] is not None:
- # attach_vpc is a list of ids used while creating
- self.module.params["attach_vpc"] = self.get_vpc_ids()
+class AnsibleVultrInstance(AnsibleVultrCommonInstance):
def handle_power_status(self, resource, state, action, power_status, force=False, wait_for_state=True):
if state == self.module.params["state"] and (resource["power_status"] != power_status or force):
self.result["changed"] = True
@@ -588,31 +443,9 @@ class AnsibleVultrInstance(AnsibleVultr):
resource = self.wait_for_state(resource=resource, key="power_status", states=[power_status])
return resource
- def create(self):
- param_keys = ("os", "image", "app", "snapshot")
- if not any(self.module.params.get(x) is not None for x in param_keys):
- self.module.fail_json(msg="missing required arguements, one of the following required: %s" % ", ".join(param_keys))
- return super(AnsibleVultrInstance, self).create()
-
- def update(self, resource):
- user_data = self.get_user_data(resource=resource)
- resource["user_data"] = user_data.encode()
-
- if self.module.params["vpcs"] is not None:
- resource["attach_vpc"] = list()
- for vpc in list(resource["vpcs"]):
- resource["attach_vpc"].append(vpc["id"])
-
- # detach_vpc is a list of ids to be detached
- resource["detach_vpc"] = list()
- self.module.params["detach_vpc"] = self.get_detach_vpcs_ids(resource=resource)
-
- return super(AnsibleVultrInstance, self).update(resource=resource)
-
def create_or_update(self):
resource = super(AnsibleVultrInstance, self).create_or_update()
if resource:
- resource = self.wait_for_state(resource=resource, key="status", states=["active"])
resource = self.wait_for_state(resource=resource, key="server_status", states=["none", "locked"], cmp="!=")
# Hanlde power status
@@ -630,10 +463,15 @@ class AnsibleVultrInstance(AnsibleVultr):
return resource
- def transform_result(self, resource):
- if resource:
- resource["user_data"] = self.get_user_data(resource=resource)
- return resource
+ def configure(self):
+ super(AnsibleVultrInstance, self).configure()
+
+ if self.module.params["state"] != "absent":
+ if self.module.params.get("firewall_group") is not None:
+ self.module.params["firewall_group_id"] = self.get_firewall_group()["id"]
+
+ if self.module.params.get("backups") is not None:
+ self.module.params["backups"] = "enabled" if self.module.params["backups"] else "disabled"
def absent(self):
resource = self.query()
@@ -666,6 +504,7 @@ def main():
user_data=dict(type="str"),
ssh_keys=dict(type="list", elements="str", no_log=False),
region=dict(type="str", required=True),
+ user_scheme=dict(type="str", choices=["root", "limited"]),
state=dict(
choices=[
"present",
@@ -713,6 +552,7 @@ def main():
"sshkey_id",
"backups",
"attach_vpc",
+ "user_scheme",
],
resource_update_param_keys=[
"plan",
diff --git a/ansible_collections/vultr/cloud/plugins/modules/instance_info.py b/ansible_collections/vultr/cloud/plugins/modules/instance_info.py
index 2a5c311bb..d473eae20 100644
--- a/ansible_collections/vultr/cloud/plugins/modules/instance_info.py
+++ b/ansible_collections/vultr/cloud/plugins/modules/instance_info.py
@@ -239,8 +239,8 @@ def main():
argument_spec = vultr_argument_spec()
argument_spec.update(
dict(
- region=dict(type="str", aliases=["name"]),
- label=dict(type="str"),
+ region=dict(type="str"),
+ label=dict(type="str", aliases=["name"]),
) # type: ignore
)
diff --git a/ansible_collections/vultr/cloud/plugins/modules/object_storage.py b/ansible_collections/vultr/cloud/plugins/modules/object_storage.py
new file mode 100644
index 000000000..32ad4f70a
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/object_storage.py
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2024, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: object_storage
+short_description: Manages object storages on Vultr
+description:
+ - Manage object storages.
+version_added: "1.12.0"
+author:
+ - "René Moser (@resmo)"
+options:
+ label:
+ description:
+ - Name of the object storage.
+ required: true
+ aliases: [ name ]
+ type: str
+ cluster:
+ description:
+ - Cluster hostname where the object storage will be created.
+ required: true
+ type: str
+ state:
+ description:
+ - State of the object storage.
+ default: present
+ choices: [ present, absent]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+---
+- name: Ensure an object storage is present
+ vultr.cloud.object_storage:
+ label: my object storage
+ cluster: ewr1.vultrobjects.com
+
+- name: Ensure an object storage is absent
+ vultr.cloud.object_storage:
+ label: my object storage
+ cluster: ewr1.vultrobjects.com
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_account:
+ description: Account used in the ini file to select the key.
+ returned: success
+ type: str
+ sample: default
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_block_storage:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ date_created:
+ description: Date the object storage was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ id:
+ description: A unique ID for the object storage.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ label:
+ description: The user-supplied label for this object storage.
+ returned: success
+ type: str
+ sample: my object storage
+ region:
+ description: The region for this object storage.
+ returned: success
+ type: str
+ sample: ews
+ status:
+ description: The status of this object storage.
+ returned: success
+ type: str
+ sample: active
+ s3_hostname:
+ description: The Cluster hostname for this object storage.
+ returned: success
+ type: str
+ sample: ewr1.vultrobjects.com
+ s3_access_key:
+ description: The object storage access key.
+ returned: success
+ type: str
+ sample: 00example11223344
+ s3_secret_key:
+ description: The object storage secret key.
+ returned: success
+ type: str
+ sample: 00example1122334455667788990011
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrObjectStorage(AnsibleVultr):
+ def configure(self):
+ super(AnsibleVultrObjectStorage, self).configure()
+ cluster = self.get_cluster()
+ self.module.params["cluster_id"] = cluster["id"]
+ # Use region to distinguish labels between regions
+ self.module.params["region"] = cluster["region"]
+
+ def get_cluster(self):
+ return self.query_filter_list_by_name(
+ key_name="hostname",
+ param_key="cluster",
+ path="/object-storage/clusters",
+ result_key="clusters",
+ fail_not_found=True,
+ )
+
+ def create_or_update(self):
+ resource = super(AnsibleVultrObjectStorage, self).create_or_update()
+ if resource:
+ resource = self.wait_for_state(resource=resource, key="status", states=["active"])
+ return resource
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ label=dict(type="str", required=True, aliases=["name"]),
+ cluster=dict(type="str", required=True),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrObjectStorage(
+ module=module,
+ namespace="vultr_object_storage",
+ resource_path="/object-storage",
+ ressource_result_key_singular="object_storage",
+ resource_create_param_keys=["label", "cluster_id"],
+ resource_update_param_keys=["label"],
+ resource_key_name="label",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py b/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py
index 9bc2e254b..4bf1c66ae 100644
--- a/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py
+++ b/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py
@@ -166,7 +166,6 @@ class AnsibleVultrReservedIp(AnsibleVultr):
instance_name = self.module.params["instance_name"]
if instance_name is not None:
-
# Empty string ID means detach instance
if len(instance_name) == 0:
return ""
@@ -187,7 +186,7 @@ class AnsibleVultrReservedIp(AnsibleVultr):
return resources["instances"][0]["id"]
- def query_list(self, path=None, result_key=None):
+ def query_list(self, path=None, result_key=None, query_params=None):
resources = self.api_query(path=self.resource_path) or dict()
resources_filtered = list()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/vpc2.py b/ansible_collections/vultr/cloud/plugins/modules/vpc2.py
new file mode 100644
index 000000000..c2caa1784
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/vpc2.py
@@ -0,0 +1,191 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: vpc2
+short_description: Manages VPCs 2.0 on Vultr
+description:
+ - Create and remove VPCs 2.0.
+version_added: "1.9.0"
+author: "René Moser (@resmo)"
+options:
+ description:
+ description:
+ - Description of the VPC.
+ required: true
+ aliases: [ name ]
+ type: str
+ ip_type:
+ description:
+ - Type of the IP version.
+ - Required if I(state=present).
+ default: v4
+ choices: [ v4 ]
+ type: str
+ ip_block:
+ description:
+ - The subnet of the VPC.
+ - Required if I(state=present).
+ type: str
+ prefix_length:
+ description:
+ - The number of bits for the netmask in CIDR notation, e.g. 24.
+ - Required if I(state=present).
+ type: int
+ region:
+ description:
+ - Region the VPC will be related to.
+ - Required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the VPC.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a VPC is present
+ vultr.cloud.vpc2:
+ description: my VPC.
+ ip_block: 10.99.1.0
+ prefix_length: 24
+ region: ewr
+
+- name: Ensure a VPC is absent
+ vultr.cloud.vpc2:
+ description: my VPC.
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_vpc2:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: my vpc
+ ip_block:
+ description: Subnet of the VPC.
+ returned: success
+ type: str
+ sample: 10.99.1.0
+ prefix_length:
+ description: The number of bits for the netmask in CIDR notation.
+ returned: success
+ type: int
+ sample: 24
+ date_created:
+ description: Date the VPC was created.
+ returned: success
+ type: str
+ sample: "2023-08-20T19:39:20+00:00"
+ region:
+ description: The region the VPC is located in.
+ returned: success
+ type: str
+ sample: ewr
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ description=dict(type="str", required=True, aliases=["name"]),
+ ip_type=dict(type="str", choices=["v4"], default="v4"),
+ ip_block=dict(type="str"),
+ prefix_length=dict(type="int"),
+ region=dict(type="str"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=(
+ (
+ "state",
+ "present",
+ ("ip_type", "ip_block", "prefix_length", "region"),
+ ),
+ ),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_vpc2",
+ resource_path="/vpc2",
+ ressource_result_key_singular="vpc",
+ resource_create_param_keys=[
+ "description",
+ "ip_type",
+ "ip_block",
+ "prefix_length",
+ "region",
+ ],
+ resource_update_param_keys=["description"],
+ resource_key_name="description",
+ resource_update_method="PUT",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/vpc2_info.py b/ansible_collections/vultr/cloud/plugins/modules/vpc2_info.py
new file mode 100644
index 000000000..c71c25d14
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/vpc2_info.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: vpc2_info
+short_description: Gather information about the Vultr VPCs 2.0
+description:
+ - Gather information about VPCs 2.0 available.
+version_added: "1.9.0"
+author:
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr VPCs 2.0 information
+ vultr.cloud.vpc2_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_vpc2_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_vpc2_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: my vpc
+ ip_block:
+ description: Subnet of the VPC.
+ returned: success
+ type: str
+ sample: 10.99.1.0
+ prefix_length:
+ description: The number of bits for the netmask in CIDR notation.
+ returned: success
+ type: int
+ sample: 24
+ date_created:
+ description: Date the VPC was created.
+ returned: success
+ type: str
+ sample: "2023-08-20T19:39:20+00:00"
+ region:
+ description: The region the VPC is located in.
+ returned: success
+ type: str
+ sample: ewr
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_vpc2_info",
+ resource_path="/vpc2",
+ ressource_result_key_singular="vpc",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()