summaryrefslogtreecommitdiffstats
path: root/ansible_collections/ngine_io/vultr/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/ngine_io/vultr/plugins
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/ngine_io/vultr/plugins')
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/doc_fragments/__init__.py0
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/doc_fragments/vultr.py58
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/inventory/__init__.py0
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/inventory/vultr.py200
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/module_utils/vultr.py336
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_account_info.py130
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage.py382
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage_info.py160
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain.py201
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain_info.py117
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_record.py376
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group.py201
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group_info.py139
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_rule.py384
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_network.py232
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_network_info.py156
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_os_info.py137
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_baremetal_info.py140
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_info.py139
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_region_info.py129
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_server.py933
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_baremetal.py548
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_info.py300
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key.py236
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key_info.py141
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script.py265
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script_info.py149
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_user.py326
-rw-r--r--ansible_collections/ngine_io/vultr/plugins/modules/vultr_user_info.py144
30 files changed, 6659 insertions, 0 deletions
diff --git a/ansible_collections/ngine_io/vultr/plugins/doc_fragments/__init__.py b/ansible_collections/ngine_io/vultr/plugins/doc_fragments/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/doc_fragments/__init__.py
diff --git a/ansible_collections/ngine_io/vultr/plugins/doc_fragments/vultr.py b/ansible_collections/ngine_io/vultr/plugins/doc_fragments/vultr.py
new file mode 100644
index 000000000..cb5cfb64f
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/doc_fragments/vultr.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 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
+
+
+class ModuleDocFragment(object):
+
+ # Standard documentation fragment
+ DOCUMENTATION = r'''
+options:
+ api_key:
+ description:
+ - API key of the Vultr API.
+ - The ENV variable C(VULTR_API_KEY) is used as default, when defined.
+ type: str
+ api_timeout:
+ description:
+ - HTTP timeout to Vultr API.
+ - The ENV variable C(VULTR_API_TIMEOUT) is used as default, when defined.
+ - Fallback value is 60 seconds if not specified.
+ type: int
+ api_retries:
+ description:
+ - Amount of retries in case of the Vultr API retuns an HTTP 503 code.
+ - The ENV variable C(VULTR_API_RETRIES) is used as default, when defined.
+ - Fallback value is 5 retries if not specified.
+ type: int
+ api_retry_max_delay:
+ description:
+ - Retry backoff delay in seconds is exponential up to this max. value, in seconds.
+ - The ENV variable C(VULTR_API_RETRY_MAX_DELAY) is used as default, when defined.
+ - Fallback value is 12 seconds.
+ type: int
+ api_account:
+ description:
+ - Name of the ini section in the C(vultr.ini) file.
+ - The ENV variable C(VULTR_API_ACCOUNT) is used as default, when defined.
+ type: str
+ default: default
+ api_endpoint:
+ description:
+ - URL to API endpint (without trailing slash).
+ - The ENV variable C(VULTR_API_ENDPOINT) is used as default, when defined.
+ - Fallback value is U(https://api.vultr.com) if not specified.
+ type: str
+ validate_certs:
+ description:
+ - Validate SSL certs of the Vultr API.
+ type: bool
+ default: yes
+requirements:
+ - python >= 2.6
+notes:
+ - Also see the API documentation on https://www.vultr.com/api/.
+'''
diff --git a/ansible_collections/ngine_io/vultr/plugins/inventory/__init__.py b/ansible_collections/ngine_io/vultr/plugins/inventory/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/inventory/__init__.py
diff --git a/ansible_collections/ngine_io/vultr/plugins/inventory/vultr.py b/ansible_collections/ngine_io/vultr/plugins/inventory/vultr.py
new file mode 100644
index 000000000..a44b47178
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/inventory/vultr.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2019, 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 = r'''
+ name: vultr
+ author:
+ - Yanis Guenane (@Spredzy)
+ - René Moser (@resmo)
+ short_description: Vultr inventory source
+ extends_documentation_fragment:
+ - constructed
+ description:
+ - Get inventory hosts from Vultr public cloud.
+ - Uses an YAML configuration file ending with either I(vultr.yml) or I(vultr.yaml) to set parameter values (also see examples).
+ - Uses I(api_config), I(~/.vultr.ini), I(./vultr.ini) or C(VULTR_API_CONFIG) pointing to a Vultr credentials INI file
+ (see U(https://docs.ansible.com/ansible/latest/scenario_guides/guide_vultr.html)).
+ options:
+ plugin:
+ description: Token that ensures this is a source file for the 'vultr' plugin.
+ type: string
+ required: True
+ choices: [ vultr ]
+ api_account:
+ description: Specify the account to be used.
+ type: string
+ default: default
+ api_config:
+ description: Path to the vultr configuration file. If not specified will be taken from regular Vultr configuration.
+ type: path
+ env:
+ - name: VULTR_API_CONFIG
+ api_key:
+ description: Vultr API key. If not specified will be taken from regular Vultr configuration.
+ type: string
+ env:
+ - name: VULTR_API_KEY
+ hostname:
+ description: Field to match the hostname. Note v4_main_ip corresponds to the main_ip field returned from the API and name to label.
+ type: string
+ default: v4_main_ip
+ choices:
+ - v4_main_ip
+ - v6_main_ip
+ - name
+ filter_by_tag:
+ description: Only return servers filtered by this tag
+ type: string
+'''
+
+EXAMPLES = r'''
+# inventory_vultr.yml file in YAML format
+# Example command line: ansible-inventory --list -i inventory_vultr.yml
+
+# Group by a region as lower case and with prefix e.g. "vultr_region_amsterdam" and by OS without prefix e.g. "CentOS_7_x64"
+plugin: vultr
+keyed_groups:
+ - prefix: vultr_region
+ key: region | lower
+ - separator: ""
+ key: os
+
+# Pass a tag filter to the API
+plugin: vultr
+filter_by_tag: Cache
+'''
+
+import json
+
+from ansible.errors import AnsibleError
+from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
+from ansible.module_utils.six.moves import configparser
+from ansible.module_utils.urls import open_url
+from ansible.module_utils._text import to_native
+from ..module_utils.vultr import Vultr, VULTR_API_ENDPOINT, VULTR_USER_AGENT
+from ansible.module_utils.six.moves.urllib.parse import quote
+
+
+SCHEMA = {
+ 'SUBID': dict(key='id'),
+ 'label': dict(key='name'),
+ 'date_created': dict(),
+ 'allowed_bandwidth_gb': dict(convert_to='float'),
+ 'auto_backups': dict(key='auto_backup_enabled', convert_to='bool'),
+ 'current_bandwidth_gb': dict(),
+ 'kvm_url': dict(),
+ 'default_password': dict(),
+ 'internal_ip': dict(),
+ 'disk': dict(),
+ 'cost_per_month': dict(convert_to='float'),
+ 'location': dict(key='region'),
+ 'main_ip': dict(key='v4_main_ip'),
+ 'network_v4': dict(key='v4_network'),
+ 'gateway_v4': dict(key='v4_gateway'),
+ 'os': dict(),
+ 'pending_charges': dict(convert_to='float'),
+ 'power_status': dict(),
+ 'ram': dict(),
+ 'plan': dict(),
+ 'server_state': dict(),
+ 'status': dict(),
+ 'firewall_group': dict(),
+ 'tag': dict(),
+ 'v6_main_ip': dict(),
+ 'v6_network': dict(),
+ 'v6_network_size': dict(),
+ 'v6_networks': dict(),
+ 'vcpu_count': dict(convert_to='int'),
+}
+
+
+def _load_conf(path, account):
+
+ if path:
+ conf = configparser.ConfigParser()
+ conf.read(path)
+
+ if not conf._sections.get(account):
+ return None
+
+ return dict(conf.items(account))
+ else:
+ return Vultr.read_ini_config(account)
+
+
+def _retrieve_servers(api_key, tag_filter=None):
+ api_url = '%s/v1/server/list' % VULTR_API_ENDPOINT
+ if tag_filter is not None:
+ api_url = api_url + '?tag=%s' % quote(tag_filter)
+
+ try:
+ response = open_url(
+ api_url, headers={'API-Key': api_key, 'Content-type': 'application/json'},
+ http_agent=VULTR_USER_AGENT,
+ )
+ servers_list = json.loads(response.read())
+
+ return servers_list.values() if servers_list else []
+ except ValueError:
+ raise AnsibleError("Incorrect JSON payload")
+ except Exception as e:
+ raise AnsibleError("Error while fetching %s: %s" % (api_url, to_native(e)))
+
+
+class InventoryModule(BaseInventoryPlugin, Constructable):
+
+ NAME = 'ngine_io.vultr.vultr'
+
+ def verify_file(self, path):
+ valid = False
+ if super(InventoryModule, self).verify_file(path):
+ if path.endswith(('vultr.yaml', 'vultr.yml')):
+ valid = True
+ return valid
+
+ def parse(self, inventory, loader, path, cache=True):
+ super(InventoryModule, self).parse(inventory, loader, path)
+ self._read_config_data(path=path)
+
+ conf = _load_conf(self.get_option('api_config'), self.get_option('api_account'))
+ try:
+ api_key = self.get_option('api_key') or conf.get('key')
+ except Exception:
+ raise AnsibleError('Could not find an API key. Check inventory file and Vultr configuration files.')
+
+ hostname_preference = self.get_option('hostname')
+
+ # Add a top group 'vultr'
+ self.inventory.add_group(group='vultr')
+
+ # Filter by tag is supported by the api with a query
+ filter_by_tag = self.get_option('filter_by_tag')
+ for server in _retrieve_servers(api_key, filter_by_tag):
+
+ server = Vultr.normalize_result(server, SCHEMA)
+
+ self.inventory.add_host(host=server['name'], group='vultr')
+
+ for attribute, value in server.items():
+ self.inventory.set_variable(server['name'], attribute, value)
+
+ if hostname_preference != 'name':
+ self.inventory.set_variable(server['name'], 'ansible_host', server[hostname_preference])
+
+ # Use constructed if applicable
+ strict = self.get_option('strict')
+
+ # Composed variables
+ self._set_composite_vars(self.get_option('compose'), server, server['name'], strict=strict)
+
+ # Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group
+ self._add_host_to_composed_groups(self.get_option('groups'), server, server['name'], strict=strict)
+
+ # Create groups based on variable values and add the corresponding hosts to it
+ self._add_host_to_keyed_groups(self.get_option('keyed_groups'), server, server['name'], strict=strict)
diff --git a/ansible_collections/ngine_io/vultr/plugins/module_utils/vultr.py b/ansible_collections/ngine_io/vultr/plugins/module_utils/vultr.py
new file mode 100644
index 000000000..81e7b62cb
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/module_utils/vultr.py
@@ -0,0 +1,336 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, René Moser <mail@renemoser.net>
+# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import os
+import time
+import random
+import urllib
+from ansible.module_utils.six.moves import configparser
+from ansible.module_utils._text import to_text, to_native
+from ansible.module_utils.urls import fetch_url
+
+
+VULTR_API_ENDPOINT = "https://api.vultr.com"
+VULTR_USER_AGENT = 'Ansible Vultr'
+
+
+def vultr_argument_spec():
+ return dict(
+ api_key=dict(type='str', default=os.environ.get('VULTR_API_KEY'), no_log=True),
+ api_timeout=dict(type='int', default=os.environ.get('VULTR_API_TIMEOUT')),
+ api_retries=dict(type='int', default=os.environ.get('VULTR_API_RETRIES')),
+ api_retry_max_delay=dict(type='int', default=os.environ.get('VULTR_API_RETRY_MAX_DELAY')),
+ api_account=dict(type='str', default=os.environ.get('VULTR_API_ACCOUNT') or 'default'),
+ api_endpoint=dict(type='str', default=os.environ.get('VULTR_API_ENDPOINT')),
+ validate_certs=dict(type='bool', default=True),
+ )
+
+
+class Vultr:
+
+ def __init__(self, module, namespace):
+
+ if module._name.startswith('vr_'):
+ module.deprecate(
+ "The Vultr modules were renamed. The prefix of the modules changed from vr_ to vultr_",
+ collection_name='ngine_io.vultr',
+ version='2.0.0') # Was Ansbile 2.11
+
+ self.module = module
+
+ # Namespace use for returns
+ self.namespace = namespace
+ self.result = {
+ 'changed': False,
+ namespace: dict(),
+ 'diff': dict(before=dict(), after=dict())
+ }
+
+ # For caching HTTP API responses
+ self.api_cache = dict()
+
+ try:
+ config = self.read_env_variables()
+ config.update(Vultr.read_ini_config(self.module.params.get('api_account')))
+ except KeyError:
+ config = {}
+
+ try:
+ self.api_config = {
+ 'api_key': self.module.params.get('api_key') or config.get('key'),
+ 'api_timeout': self.module.params.get('api_timeout') or int(config.get('timeout') or 60),
+ 'api_retries': self.module.params.get('api_retries') or int(config.get('retries') or 5),
+ 'api_retry_max_delay': self.module.params.get('api_retry_max_delay') or int(config.get('retry_max_delay') or 12),
+ 'api_endpoint': self.module.params.get('api_endpoint') or config.get('endpoint') or VULTR_API_ENDPOINT,
+ }
+ except ValueError as e:
+ self.fail_json(msg="One of the following settings, "
+ "in section '%s' in the ini config file has not an int value: timeout, retries. "
+ "Error was %s" % (self.module.params.get('api_account'), to_native(e)))
+
+ if not self.api_config.get('api_key'):
+ self.module.fail_json(msg="No API key was specified. Please refer to the documentation.")
+
+ # Common vultr returns
+ self.result['vultr_api'] = {
+ 'api_account': self.module.params.get('api_account'),
+ 'api_timeout': self.api_config['api_timeout'],
+ 'api_retries': self.api_config['api_retries'],
+ 'api_retry_max_delay': self.api_config['api_retry_max_delay'],
+ 'api_endpoint': self.api_config['api_endpoint'],
+ }
+
+ # Headers to be passed to the API
+ self.headers = {
+ 'API-Key': "%s" % self.api_config['api_key'],
+ 'User-Agent': VULTR_USER_AGENT,
+ 'Accept': 'application/json',
+ }
+
+ def read_env_variables(self):
+ keys = ['key', 'timeout', 'retries', 'retry_max_delay', 'endpoint']
+ env_conf = {}
+ for key in keys:
+ if 'VULTR_API_%s' % key.upper() not in os.environ:
+ continue
+ env_conf[key] = os.environ['VULTR_API_%s' % key.upper()]
+
+ return env_conf
+
+ @staticmethod
+ def read_ini_config(ini_group):
+ paths = (
+ os.path.join(os.path.expanduser('~'), '.vultr.ini'),
+ os.path.join(os.getcwd(), 'vultr.ini'),
+ )
+ if 'VULTR_API_CONFIG' in os.environ:
+ paths += (os.path.expanduser(os.environ['VULTR_API_CONFIG']),)
+
+ conf = configparser.ConfigParser()
+ conf.read(paths)
+
+ if not conf._sections.get(ini_group):
+ return dict()
+
+ return dict(conf.items(ini_group))
+
+ def fail_json(self, **kwargs):
+ self.result.update(kwargs)
+ self.module.fail_json(**self.result)
+
+ def get_yes_or_no(self, key):
+ if self.module.params.get(key) is not None:
+ return 'yes' if self.module.params.get(key) is True else 'no'
+
+ def switch_enable_disable(self, resource, param_key, resource_key=None):
+ if resource_key is None:
+ resource_key = param_key
+
+ param = self.module.params.get(param_key)
+ if param is None:
+ return
+
+ r_value = resource.get(resource_key)
+ if r_value in ['yes', 'no']:
+ if param and r_value != 'yes':
+ return "enable"
+ elif not param and r_value != 'no':
+ return "disable"
+ else:
+ if param and not r_value:
+ return "enable"
+ elif not param and r_value:
+ return "disable"
+
+ def api_query(self, path="/", method="GET", data=None):
+ url = self.api_config['api_endpoint'] + path
+
+ if data:
+ data_encoded = dict()
+ data_list = ""
+ for k, v in data.items():
+ if isinstance(v, list):
+ for s in v:
+ try:
+ data_list += '&%s[]=%s' % (k, urllib.quote(s))
+ except AttributeError:
+ data_list += '&%s[]=%s' % (k, urllib.parse.quote(s))
+ elif v is not None:
+ data_encoded[k] = v
+ try:
+ data = urllib.urlencode(data_encoded) + data_list
+ except AttributeError:
+ data = urllib.parse.urlencode(data_encoded) + data_list
+
+ retry_max_delay = self.api_config['api_retry_max_delay']
+ randomness = random.randint(0, 1000) / 1000.0
+
+ for retry in range(0, self.api_config['api_retries']):
+ response, info = fetch_url(
+ module=self.module,
+ url=url,
+ data=data,
+ method=method,
+ headers=self.headers,
+ timeout=self.api_config['api_timeout'],
+ )
+
+ if info.get('status') == 200:
+ break
+
+ # Vultr has a rate limiting requests per second, try to be polite
+ # Use exponential backoff plus a little bit of randomness
+ delay = 2 ** retry + randomness
+ if delay > retry_max_delay:
+ delay = retry_max_delay + randomness
+ time.sleep(delay)
+
+ else:
+ self.fail_json(msg="Reached API retries limit %s for URL %s, method %s with data %s. Returned %s, with body: %s %s" % (
+ self.api_config['api_retries'],
+ url,
+ method,
+ data,
+ info['status'],
+ info['msg'],
+ info.get('body')
+ ))
+
+ if info.get('status') != 200:
+ self.fail_json(msg="URL %s, method %s with data %s. Returned %s, with body: %s %s" % (
+ url,
+ method,
+ data,
+ info['status'],
+ info['msg'],
+ info.get('body')
+ ))
+
+ res = response.read()
+ if not res:
+ return {}
+
+ try:
+ return self.module.from_json(to_native(res)) or {}
+ 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, id_key=None, optional=False):
+ if not value:
+ return {}
+
+ r_list = None
+ if use_cache:
+ r_list = self.api_cache.get(resource)
+
+ 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):
+ 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):
+ return r_data
+ if id_key is not None and to_text(r_data[id_key]) == to_text(value):
+ return r_data
+ if not optional:
+ 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)
+ return {}
+
+ @staticmethod
+ def normalize_result(resource, schema, remove_missing_keys=True):
+ if remove_missing_keys:
+ fields_to_remove = set(resource.keys()) - set(schema.keys())
+ for field in fields_to_remove:
+ resource.pop(field)
+
+ for search_key, config in schema.items():
+ if search_key in resource:
+ if 'convert_to' in config:
+ if config['convert_to'] == 'int':
+ resource[search_key] = int(resource[search_key])
+ elif config['convert_to'] == 'float':
+ resource[search_key] = float(resource[search_key])
+ elif config['convert_to'] == 'bool':
+ resource[search_key] = True if resource[search_key] == 'yes' else False
+
+ if 'transform' in config:
+ resource[search_key] = config['transform'](resource[search_key])
+
+ if 'key' in config:
+ resource[config['key']] = resource[search_key]
+ del resource[search_key]
+
+ return resource
+
+ def get_result(self, resource):
+ if resource:
+ if isinstance(resource, list):
+ self.result[self.namespace] = [Vultr.normalize_result(item, self.returns) for item in resource]
+ else:
+ self.result[self.namespace] = Vultr.normalize_result(resource, self.returns)
+
+ return self.result
+
+ def get_plan(self, plan=None, key='name', optional=False):
+ value = plan or self.module.params.get('plan')
+
+ return self.query_resource_by_key(
+ key=key,
+ value=value,
+ resource='plans',
+ use_cache=True,
+ id_key='VPSPLANID',
+ optional=optional,
+ )
+
+ def get_firewallgroup(self, firewallgroup=None, key='description'):
+ value = firewallgroup or self.module.params.get('firewallgroup')
+
+ return self.query_resource_by_key(
+ key=key,
+ value=value,
+ resource='firewall',
+ query_by='group_list',
+ use_cache=True
+ )
+
+ def get_application(self, application=None, key='name'):
+ value = application or self.module.params.get('application')
+
+ return self.query_resource_by_key(
+ key=key,
+ value=value,
+ resource='app',
+ use_cache=True
+ )
+
+ def get_region(self, region=None, key='name'):
+ value = region or self.module.params.get('region')
+
+ return self.query_resource_by_key(
+ key=key,
+ value=value,
+ resource='regions',
+ use_cache=True
+ )
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/__init__.py b/ansible_collections/ngine_io/vultr/plugins/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/__init__.py
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_account_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_account_info.py
new file mode 100644
index 000000000..1718ff664
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_account_info.py
@@ -0,0 +1,130 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019, 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 = r'''
+---
+module: vultr_account_info
+short_description: Get information about the Vultr account.
+description:
+ - Get infos about account balance, charges and payments.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Get Vultr account infos
+ ngine_io.vultr.vultr_account_info:
+ register: result
+
+- name: Print the infos
+ debug:
+ var: result.vultr_account_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_account_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ balance:
+ description: Your account balance.
+ returned: success
+ type: float
+ sample: -214.69
+ pending_charges:
+ description: Charges pending.
+ returned: success
+ type: float
+ sample: 57.03
+ last_payment_date:
+ description: Date of the last payment.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ last_payment_amount:
+ description: The amount of the last payment transaction.
+ returned: success
+ type: float
+ sample: -250.0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrAccountInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrAccountInfo, self).__init__(module, "vultr_account_info")
+
+ self.returns = {
+ 'balance': dict(convert_to='float'),
+ 'pending_charges': dict(convert_to='float'),
+ 'last_payment_date': dict(),
+ 'last_payment_amount': dict(convert_to='float'),
+ }
+
+ def get_account_info(self):
+ return self.api_query(path="/v1/account/info")
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ account_info = AnsibleVultrAccountInfo(module)
+ result = account_info.get_result(account_info.get_account_info())
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage.py
new file mode 100644
index 000000000..5cda1cd1a
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage.py
@@ -0,0 +1,382 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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: vultr_block_storage
+short_description: Manages block storage volumes on Vultr.
+description:
+ - Manage block storage volumes on Vultr.
+author: "Yanis Guenane (@Spredzy)"
+version_added: "0.1.0"
+options:
+ name:
+ description:
+ - Name of the block storage volume.
+ required: true
+ aliases: [ description, label ]
+ type: str
+ size:
+ description:
+ - Size of the block storage volume in GB.
+ - Required if I(state) is present.
+ - If it's larger than the volume's current size, the volume will be resized.
+ type: int
+ region:
+ description:
+ - Region the block storage volume is deployed into.
+ - Required if I(state) is present.
+ type: str
+ state:
+ description:
+ - State of the block storage volume.
+ default: present
+ choices: [ present, absent, attached, detached ]
+ type: str
+ attached_to_SUBID:
+ description:
+ - The ID of the server the volume is attached to.
+ - Required if I(state) is attached.
+ aliases: [ attached_to_id ]
+ type: int
+ live_attachment:
+ description:
+ - Whether the volume should be attached/detached, even if the server not stopped.
+ type: bool
+ default: True
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: Ensure a block storage volume is present
+ ngine_io.vultr.vultr_block_storage:
+ name: myvolume
+ size: 10
+ region: Amsterdam
+
+- name: Ensure a block storage volume is absent
+ ngine_io.vultr.vultr_block_storage:
+ name: myvolume
+ state: absent
+
+- name: Ensure a block storage volume exists and is attached to server 114
+ ngine_io.vultr.vultr_block_storage:
+ name: myvolume
+ state: attached
+ attached_to_id: 114
+ size: 10
+
+- name: Ensure a block storage volume exists and is not attached to any server
+ ngine_io.vultr.vultr_block_storage:
+ name: myvolume
+ state: detached
+ size: 10
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_block_storage:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ attached_to_id:
+ description: The ID of the server the volume is attached to
+ returned: success
+ type: str
+ sample: "10194376"
+ cost_per_month:
+ description: Cost per month for the volume
+ returned: success
+ type: float
+ sample: 1.00
+ date_created:
+ description: Date when the volume was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ id:
+ description: ID of the block storage volume
+ returned: success
+ type: str
+ sample: "1234abcd"
+ name:
+ description: Name of the volume
+ returned: success
+ type: str
+ sample: "ansible-test-volume"
+ region:
+ description: Region the volume was deployed into
+ returned: success
+ type: str
+ sample: "New Jersey"
+ size:
+ description: Information about the volume size in GB
+ returned: success
+ type: int
+ sample: 10
+ status:
+ description: Status about the deployment of the volume
+ returned: success
+ type: str
+ sample: "active"
+
+'''
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrBlockStorage(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrBlockStorage, self).__init__(module, "vultr_block_storage")
+
+ self.returns = {
+ 'SUBID': dict(key='id'),
+ 'label': dict(key='name'),
+ 'DCID': dict(key='region', transform=self._get_region_name),
+ 'attached_to_SUBID': dict(key='attached_to_id'),
+ 'cost_per_month': dict(convert_to='float'),
+ 'date_created': dict(),
+ 'size_gb': dict(key='size', convert_to='int'),
+ 'status': dict()
+ }
+
+ def _get_region_name(self, region):
+ return self.get_region(region, 'DCID').get('name')
+
+ def get_block_storage_volumes(self):
+ volumes = self.api_query(path="/v1/block/list")
+ if volumes:
+ for volume in volumes:
+ if volume.get('label') == self.module.params.get('name'):
+ return volume
+ return {}
+
+ def present_block_storage_volume(self):
+ volume = self.get_block_storage_volumes()
+ if not volume:
+ volume = self._create_block_storage_volume(volume)
+ return volume
+
+ def _create_block_storage_volume(self, volume):
+ self.result['changed'] = True
+ data = {
+ 'label': self.module.params.get('name'),
+ 'DCID': self.get_region().get('DCID'),
+ 'size_gb': self.module.params.get('size')
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/block/create",
+ method="POST",
+ data=data
+ )
+ volume = self.get_block_storage_volumes()
+ return volume
+
+ def absent_block_storage_volume(self):
+ volume = self.get_block_storage_volumes()
+ if volume:
+ self.result['changed'] = True
+
+ data = {
+ 'SUBID': volume['SUBID'],
+ }
+
+ self.result['diff']['before'] = volume
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/block/delete",
+ method="POST",
+ data=data
+ )
+ return volume
+
+ def detached_block_storage_volume(self):
+ volume = self.present_block_storage_volume()
+ if volume.get('attached_to_SUBID') is None:
+ return volume
+
+ self.result['changed'] = True
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': volume['SUBID'],
+ 'live': self.get_yes_or_no('live_attachment')
+ }
+ self.api_query(
+ path='/v1/block/detach',
+ method='POST',
+ data=data
+ )
+
+ volume = self.get_block_storage_volumes()
+ else:
+ volume['attached_to_SUBID'] = None
+
+ self.result['diff']['after'] = volume
+
+ return volume
+
+ def attached_block_storage_volume(self):
+ expected_server = self.module.params.get('attached_to_SUBID')
+ volume = self.present_block_storage_volume()
+ server = volume.get('attached_to_SUBID')
+ if server == expected_server:
+ return volume
+
+ if server is not None:
+ self.module.fail_json(
+ msg='Volume already attached to server %s' % server
+ )
+
+ self.result['changed'] = True
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': volume['SUBID'],
+ # This API call expects a param called attach_to_SUBID,
+ # but all the BlockStorage API response payloads call
+ # this parameter attached_to_SUBID. So we'll standardize
+ # to the latter and attached_to_id, but we'll pass the
+ # expected attach_to_SUBID to this API call.
+ 'attach_to_SUBID': expected_server,
+ 'live': self.get_yes_or_no('live_attachment'),
+ }
+ self.api_query(
+ path='/v1/block/attach',
+ method='POST',
+ data=data
+ )
+ volume = self.get_block_storage_volumes()
+ else:
+ volume['attached_to_SUBID'] = expected_server
+
+ self.result['diff']['after'] = volume
+
+ return volume
+
+ def ensure_volume_size(self, volume, expected_size):
+ curr_size = volume.get('size_gb')
+ # When creating, attaching, or detaching a volume in check_mode,
+ # sadly, size_gb doesn't exist, because those methods return the
+ # result of get_block_storage_volumes, which is {} on check_mode.
+ if curr_size is None or curr_size >= expected_size:
+ # we only resize volumes that are smaller than
+ # expected. There's no shrinking operation.
+ return volume
+
+ self.result['changed'] = True
+
+ volume['size_gb'] = expected_size
+ self.result['diff']['after'] = volume
+
+ if not self.module.check_mode:
+ data = {'SUBID': volume['SUBID'], 'size_gb': expected_size}
+ self.api_query(
+ path='/v1/block/resize',
+ method='POST',
+ data=data,
+ )
+
+ return volume
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True, aliases=['description', 'label']),
+ size=dict(type='int'),
+ region=dict(type='str'),
+ state=dict(
+ type='str',
+ choices=['present', 'absent', 'attached', 'detached'],
+ default='present'
+ ),
+ attached_to_SUBID=dict(type='int', aliases=['attached_to_id']),
+ live_attachment=dict(type='bool', default=True)
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ['state', 'present', ['size', 'region']],
+ ['state', 'detached', ['size', 'region']],
+ ['state', 'attached', ['size', 'region', 'attached_to_SUBID']],
+ ]
+ )
+
+ vultr_block_storage = AnsibleVultrBlockStorage(module)
+
+ desired_state = module.params.get('state')
+ if desired_state == "absent":
+ volume = vultr_block_storage.absent_block_storage_volume()
+ elif desired_state == 'attached':
+ volume = vultr_block_storage.attached_block_storage_volume()
+ elif desired_state == 'detached':
+ volume = vultr_block_storage.detached_block_storage_volume()
+ else:
+ volume = vultr_block_storage.present_block_storage_volume()
+
+ expected_size = module.params.get('size')
+ if expected_size and desired_state != 'absent':
+ volume = vultr_block_storage.ensure_volume_size(
+ volume,
+ expected_size
+ )
+
+ result = vultr_block_storage.get_result(volume)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage_info.py
new file mode 100644
index 000000000..46bdbecb3
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_block_storage_info.py
@@ -0,0 +1,160 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2019, 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 = r'''
+---
+module: vultr_block_storage_info
+short_description: Get information about the Vultr block storage volumes available.
+description:
+ - Get infos about block storage volumes available in Vultr.
+version_added: "0.1.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Get Vultr block storage infos
+ ngine_io.vultr.vultr_block_storage_info:
+ register: result
+
+- name: Print the infos
+ debug:
+ var: result.vultr_block_storage_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_block_storage_info:
+ description: Response from Vultr API as list
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the block storage.
+ returned: success
+ type: int
+ sample: 17332323
+ size:
+ description: Size in GB of the block storage.
+ returned: success
+ type: int
+ sample: 10
+ region:
+ description: Region the block storage is located in.
+ returned: success
+ type: str
+ sample: New Jersey
+ name:
+ description: Name of the block storage.
+ returned: success
+ type: str
+ sample: my volume
+ cost_per_month:
+ description: Cost per month of the block storage.
+ returned: success
+ type: float
+ sample: 1.0
+ date_created:
+ description: Date created of the block storage.
+ returned: success
+ type: str
+ sample: "2018-07-24 12:59:59"
+ status:
+ description: Status of the block storage.
+ returned: success
+ type: str
+ sample: active
+ attached_to_id:
+ description: Block storage is attached to this server ID.
+ returned: success
+ type: str
+ sample: null
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrBlockStorageFacts(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrBlockStorageFacts, self).__init__(module, "vultr_block_storage_info")
+
+ self.returns = {
+ 'attached_to_SUBID': dict(key='attached_to_id'),
+ 'cost_per_month': dict(convert_to='float'),
+ 'date_created': dict(),
+ 'SUBID': dict(key='id'),
+ 'label': dict(key='name'),
+ 'DCID': dict(key='region', transform=self._get_region_name),
+ 'size_gb': dict(key='size', convert_to='int'),
+ 'status': dict()
+ }
+
+ def _get_region_name(self, region):
+ return self.get_region(region, 'DCID').get('name')
+
+ def get_block_storage_volumes(self):
+ return self.api_query(path="/v1/block/list")
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ volume_info = AnsibleVultrBlockStorageFacts(module)
+ result = volume_info.get_result(volume_info.get_block_storage_volumes())
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain.py
new file mode 100644
index 000000000..bb83d3733
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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 = r'''
+---
+module: vultr_dns_domain
+short_description: Manages DNS domains on Vultr.
+description:
+ - Create and remove DNS domains.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - The domain name.
+ required: true
+ aliases: [ domain ]
+ type: str
+ server_ip:
+ description:
+ - The default server IP.
+ - Use M(ngine_io.vultr.vultr_dns_record) to change it once the domain is created.
+ - Required if C(state=present).
+ type: str
+ state:
+ description:
+ - State of the DNS domain.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Ensure a domain exists
+ ngine_io.vultr.vultr_dns_domain:
+ name: example.com
+ server_ip: 10.10.10.10
+
+- name: Ensure a domain is absent
+ ngine_io.vultr.vultr_dns_domain:
+ name: example.com
+ state: absent
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_dns_domain:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ name:
+ description: Name of the DNS Domain.
+ returned: success
+ type: str
+ sample: example.com
+ date_created:
+ description: Date the DNS domain was created.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr import Vultr, vultr_argument_spec
+
+
+class AnsibleVultrDnsDomain(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrDnsDomain, self).__init__(module, "vultr_dns_domain")
+
+ self.returns = {
+ 'domain': dict(key='name'),
+ 'date_created': dict(),
+ }
+
+ def get_domain(self):
+ domains = self.api_query(path="/v1/dns/list")
+ name = self.module.params.get('name').lower()
+ if domains:
+ for domain in domains:
+ if domain.get('domain').lower() == name:
+ return domain
+ return {}
+
+ def present_domain(self):
+ domain = self.get_domain()
+ if not domain:
+ domain = self._create_domain(domain)
+ return domain
+
+ def _create_domain(self, domain):
+ self.result['changed'] = True
+ data = {
+ 'domain': self.module.params.get('name'),
+ 'serverip': self.module.params.get('server_ip'),
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/dns/create_domain",
+ method="POST",
+ data=data
+ )
+ domain = self.get_domain()
+ return domain
+
+ def absent_domain(self):
+ domain = self.get_domain()
+ if domain:
+ self.result['changed'] = True
+
+ data = {
+ 'domain': domain['domain'],
+ }
+
+ self.result['diff']['before'] = domain
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/dns/delete_domain",
+ method="POST",
+ data=data
+ )
+ return domain
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True, aliases=['domain']),
+ server_ip=dict(type='str',),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ('state', 'present', ['server_ip']),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr_domain = AnsibleVultrDnsDomain(module)
+ if module.params.get('state') == "absent":
+ domain = vultr_domain.absent_domain()
+ else:
+ domain = vultr_domain.present_domain()
+
+ result = vultr_domain.get_result(domain)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain_info.py
new file mode 100644
index 000000000..35a47d701
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_domain_info.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_dns_domain_info
+short_description: Gather information about the Vultr DNS domains available.
+description:
+ - Gather information about DNS domains available in Vultr.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr DNS domains information
+ ngine_io.vultr.vultr_dns_domains_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_dns_domain_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_dns_domain_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ domain:
+ description: Name of the DNS Domain.
+ returned: success
+ type: str
+ sample: example.com
+ date_created:
+ description: Date the DNS domain was created.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrDnsDomainInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrDnsDomainInfo, self).__init__(module, "vultr_dns_domain_info")
+
+ self.returns = {
+ "date_created": dict(),
+ "domain": dict(),
+ }
+
+ def get_domains(self):
+ return self.api_query(path="/v1/dns/list")
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ domain_info = AnsibleVultrDnsDomainInfo(module)
+ result = domain_info.get_result(domain_info.get_domains())
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_record.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_record.py
new file mode 100644
index 000000000..bab11c4c0
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_dns_record.py
@@ -0,0 +1,376 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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: vultr_dns_record
+short_description: Manages DNS records on Vultr.
+description:
+ - Create, update and remove DNS records.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - The record name (subrecord).
+ default: ""
+ aliases: [ subrecord ]
+ type: str
+ domain:
+ description:
+ - The domain the record is related to.
+ type: str
+ required: true
+ record_type:
+ description:
+ - Type of the record.
+ default: A
+ choices:
+ - A
+ - AAAA
+ - CNAME
+ - MX
+ - SRV
+ - CAA
+ - TXT
+ - NS
+ - SSHFP
+ aliases: [ type ]
+ type: str
+ data:
+ description:
+ - Data of the record.
+ - Required if C(state=present) or C(multiple=yes).
+ type: str
+ ttl:
+ description:
+ - TTL of the record.
+ default: 300
+ type: int
+ multiple:
+ description:
+ - Whether to use more than one record with similar C(name) including no name and C(record_type).
+ - Only allowed for a few record types, e.g. C(record_type=A), C(record_type=NS) or C(record_type=MX).
+ - C(data) will not be updated, instead it is used as a key to find existing records.
+ default: no
+ type: bool
+ priority:
+ description:
+ - Priority of the record.
+ default: 0
+ type: int
+ state:
+ description:
+ - State of the DNS record.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: Ensure an A record exists
+ ngine_io.vultr.vultr_dns_record:
+ name: www
+ domain: example.com
+ data: 10.10.10.10
+ ttl: 3600
+
+- name: Ensure a second A record exists for round robin LB
+ ngine_io.vultr.vultr_dns_record:
+ name: www
+ domain: example.com
+ data: 10.10.10.11
+ ttl: 60
+ multiple: yes
+
+- name: Ensure a CNAME record exists
+ ngine_io.vultr.vultr_dns_record:
+ name: web
+ record_type: CNAME
+ domain: example.com
+ data: www.example.com
+
+- name: Ensure MX record exists
+ ngine_io.vultr.vultr_dns_record:
+ record_type: MX
+ domain: example.com
+ data: "{{ item.data }}"
+ priority: "{{ item.priority }}"
+ multiple: yes
+ with_items:
+ - { data: mx1.example.com, priority: 10 }
+ - { data: mx2.example.com, priority: 10 }
+ - { data: mx3.example.com, priority: 20 }
+
+- name: Ensure a record is absent
+ ngine_io.vultr.vultr_dns_record:
+ name: www
+ domain: example.com
+ state: absent
+
+- name: Ensure MX record is absent in case multiple exists
+ ngine_io.vultr.vultr_dns_record:
+ record_type: MX
+ domain: example.com
+ data: mx1.example.com
+ multiple: yes
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_dns_record:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: The ID of the DNS record.
+ returned: success
+ type: int
+ sample: 1265277
+ name:
+ description: The name of the DNS record.
+ returned: success
+ type: str
+ sample: web
+ record_type:
+ description: The name of the DNS record.
+ returned: success
+ type: str
+ sample: web
+ data:
+ description: Data of the DNS record.
+ returned: success
+ type: str
+ sample: 10.10.10.10
+ domain:
+ description: Domain the DNS record is related to.
+ returned: success
+ type: str
+ sample: example.com
+ priority:
+ description: Priority of the DNS record.
+ returned: success
+ type: int
+ sample: 10
+ ttl:
+ description: Time to live of the DNS record.
+ returned: success
+ type: int
+ sample: 300
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+RECORD_TYPES = [
+ 'A',
+ 'AAAA',
+ 'CNAME',
+ 'MX',
+ 'TXT',
+ 'NS',
+ 'SRV',
+ 'CAA',
+ 'SSHFP'
+]
+
+
+class AnsibleVultrDnsRecord(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrDnsRecord, self).__init__(module, "vultr_dns_record")
+
+ self.returns = {
+ 'RECORDID': dict(key='id'),
+ 'name': dict(),
+ 'record': dict(),
+ 'priority': dict(),
+ 'data': dict(),
+ 'type': dict(key='record_type'),
+ 'ttl': dict(),
+ }
+
+ def get_record(self):
+ records = self.api_query(path="/v1/dns/records?domain=%s" % self.module.params.get('domain'))
+
+ multiple = self.module.params.get('multiple')
+ data = self.module.params.get('data')
+ name = self.module.params.get('name')
+ record_type = self.module.params.get('record_type')
+
+ result = {}
+ for record in records or []:
+ if record.get('type') != record_type:
+ continue
+
+ if record.get('name') == name:
+ if not multiple:
+ if result:
+ self.module.fail_json(msg="More than one record with record_type=%s and name=%s params. "
+ "Use multiple=yes for more than one record." % (record_type, name))
+ else:
+ result = record
+ elif record.get('data') == data:
+ return record
+
+ return result
+
+ def present_record(self):
+ record = self.get_record()
+ if not record:
+ record = self._create_record(record)
+ else:
+ record = self._update_record(record)
+ return record
+
+ def _create_record(self, record):
+ self.result['changed'] = True
+ data = {
+ 'name': self.module.params.get('name'),
+ 'domain': self.module.params.get('domain'),
+ 'data': self.module.params.get('data'),
+ 'type': self.module.params.get('record_type'),
+ 'priority': self.module.params.get('priority'),
+ 'ttl': self.module.params.get('ttl'),
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/dns/create_record",
+ method="POST",
+ data=data
+ )
+ record = self.get_record()
+ return record
+
+ def _update_record(self, record):
+ data = {
+ 'RECORDID': record['RECORDID'],
+ 'name': self.module.params.get('name'),
+ 'domain': self.module.params.get('domain'),
+ 'data': self.module.params.get('data'),
+ 'type': self.module.params.get('record_type'),
+ 'priority': self.module.params.get('priority'),
+ 'ttl': self.module.params.get('ttl'),
+ }
+ has_changed = [k for k in data if k in record and data[k] != record[k]]
+ if has_changed:
+ self.result['changed'] = True
+
+ self.result['diff']['before'] = record
+ self.result['diff']['after'] = record.copy()
+ self.result['diff']['after'].update(data)
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/dns/update_record",
+ method="POST",
+ data=data
+ )
+ record = self.get_record()
+ return record
+
+ def absent_record(self):
+ record = self.get_record()
+ if record:
+ self.result['changed'] = True
+
+ data = {
+ 'RECORDID': record['RECORDID'],
+ 'domain': self.module.params.get('domain'),
+ }
+
+ self.result['diff']['before'] = record
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/dns/delete_record",
+ method="POST",
+ data=data
+ )
+ return record
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ domain=dict(type='str', required=True),
+ name=dict(type='str', default="", aliases=['subrecord']),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ttl=dict(type='int', default=300),
+ record_type=dict(type='str', choices=RECORD_TYPES, default='A', aliases=['type']),
+ multiple=dict(type='bool', default=False),
+ priority=dict(type='int', default=0),
+ data=dict(type='str',)
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ('state', 'present', ['data']),
+ ('multiple', True, ['data']),
+ ],
+
+ supports_check_mode=True,
+ )
+
+ vultr_record = AnsibleVultrDnsRecord(module)
+ if module.params.get('state') == "absent":
+ record = vultr_record.absent_record()
+ else:
+ record = vultr_record.present_record()
+
+ result = vultr_record.get_result(record)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group.py
new file mode 100644
index 000000000..36ef3b435
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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: vultr_firewall_group
+short_description: Manages firewall groups on Vultr.
+description:
+ - Create and remove firewall groups.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the firewall group.
+ required: true
+ aliases: [ description ]
+ type: str
+ state:
+ description:
+ - State of the firewall group.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: ensure a firewall group is present
+ ngine_io.vultr.vultr_firewall_group:
+ name: my http firewall
+
+- name: ensure a firewall group is absent
+ ngine_io.vultr.vultr_firewall_group:
+ name: my http firewall
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_firewall_group:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the firewall group
+ returned: success
+ type: str
+ sample: 1234abcd
+ name:
+ description: Name of the firewall group
+ returned: success
+ type: str
+ sample: my firewall group
+ date_created:
+ description: Date the firewall group was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ date_modified:
+ description: Date the firewall group was modified
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrFirewallGroup(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrFirewallGroup, self).__init__(module, "vultr_firewall_group")
+
+ self.returns = {
+ 'FIREWALLGROUPID': dict(key='id'),
+ 'description': dict(key='name'),
+ 'date_created': dict(),
+ 'date_modified': dict(),
+ }
+
+ def get_firewall_group(self):
+ firewall_groups = self.api_query(path="/v1/firewall/group_list")
+ if firewall_groups:
+ for firewall_group_id, firewall_group_data in firewall_groups.items():
+ if firewall_group_data.get('description') == self.module.params.get('name'):
+ return firewall_group_data
+ return {}
+
+ def present_firewall_group(self):
+ firewall_group = self.get_firewall_group()
+ if not firewall_group:
+ firewall_group = self._create_firewall_group(firewall_group)
+ return firewall_group
+
+ def _create_firewall_group(self, firewall_group):
+ self.result['changed'] = True
+ data = {
+ 'description': self.module.params.get('name'),
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/firewall/group_create",
+ method="POST",
+ data=data
+ )
+ firewall_group = self.get_firewall_group()
+ return firewall_group
+
+ def absent_firewall_group(self):
+ firewall_group = self.get_firewall_group()
+ if firewall_group:
+ self.result['changed'] = True
+
+ data = {
+ 'FIREWALLGROUPID': firewall_group['FIREWALLGROUPID'],
+ }
+
+ self.result['diff']['before'] = firewall_group
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/firewall/group_delete",
+ method="POST",
+ data=data
+ )
+ return firewall_group
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True, aliases=['description']),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr_firewall_group = AnsibleVultrFirewallGroup(module)
+ if module.params.get('state') == "absent":
+ firewall_group = vultr_firewall_group.absent_firewall_group()
+ else:
+ firewall_group = vultr_firewall_group.present_firewall_group()
+
+ result = vultr_firewall_group.get_result(firewall_group)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group_info.py
new file mode 100644
index 000000000..52b3eb0ae
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_group_info.py
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_firewall_group_info
+short_description: Gather information about the Vultr firewall groups available.
+description:
+ - Gather information about firewall groups available in Vultr.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr firewall groups information
+ ngine_io.vultr.vultr_firewall_group_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_firewall_group_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_firewall_group_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the firewall group
+ returned: success
+ type: str
+ sample: 1234abcd
+ description:
+ description: Name of the firewall group
+ returned: success
+ type: str
+ sample: my firewall group
+ date_created:
+ description: Date the firewall group was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ date_modified:
+ description: Date the firewall group was modified
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrFirewallGroupInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrFirewallGroupInfo, self).__init__(module, "vultr_firewall_group_info")
+
+ self.returns = {
+ "FIREWALLGROUPID": dict(key='id'),
+ "date_created": dict(),
+ "date_modified": dict(),
+ "description": dict(),
+ "instance_count": dict(convert_to='int'),
+ "max_rule_count": dict(convert_to='int'),
+ "rule_count": dict(convert_to='int')
+ }
+
+ def get_firewall_group(self):
+ return self.api_query(path="/v1/firewall/group_list")
+
+
+def parse_fw_group_list(fwgroups_list):
+ if not fwgroups_list:
+ return []
+
+ return [group for id, group in fwgroups_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ fw_group_info = AnsibleVultrFirewallGroupInfo(module)
+ result = fw_group_info.get_result(parse_fw_group_list(fw_group_info.get_firewall_group()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_rule.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_rule.py
new file mode 100644
index 000000000..f9a59b2b0
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_firewall_rule.py
@@ -0,0 +1,384 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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: vultr_firewall_rule
+short_description: Manages firewall rules on Vultr.
+description:
+ - Create and remove firewall rules.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ group:
+ description:
+ - Name of the firewall group.
+ required: true
+ type: str
+ ip_version:
+ description:
+ - IP address version
+ choices: [ v4, v6 ]
+ default: v4
+ aliases: [ ip_type ]
+ type: str
+ protocol:
+ description:
+ - Protocol of the firewall rule.
+ choices: [ icmp, tcp, udp, gre ]
+ default: tcp
+ type: str
+ cidr:
+ description:
+ - Network in CIDR format
+ - The CIDR format must match with the C(ip_version) value.
+ - Required if C(state=present).
+ - Defaulted to 0.0.0.0/0 or ::/0 depending on C(ip_version).
+ type: str
+ start_port:
+ description:
+ - Start port for the firewall rule.
+ - Required if C(protocol) is tcp or udp and I(state=present).
+ aliases: [ port ]
+ type: int
+ end_port:
+ description:
+ - End port for the firewall rule.
+ - Only considered if C(protocol) is tcp or udp and I(state=present).
+ type: int
+ state:
+ description:
+ - State of the firewall rule.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: ensure a firewall rule is present
+ ngine_io.vultr.vultr_firewall_rule:
+ group: application
+ protocol: tcp
+ start_port: 8000
+ end_port: 9000
+ cidr: 17.17.17.0/24
+
+- name: open DNS port for all ipv4 and ipv6
+ ngine_io.vultr.vultr_firewall_rule:
+ group: dns
+ protocol: udp
+ port: 53
+ ip_version: "{{ item }}"
+ with_items: [ v4, v6 ]
+
+- name: allow ping
+ ngine_io.vultr.vultr_firewall_rule:
+ group: web
+ protocol: icmp
+
+- name: ensure a firewall rule is absent
+ ngine_io.vultr.vultr_firewall_rule:
+ group: application
+ protocol: tcp
+ start_port: 8000
+ end_port: 9000
+ cidr: 17.17.17.0/24
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_firewall_rule:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ rule_number:
+ description: Rule number of the firewall rule
+ returned: success
+ type: int
+ sample: 2
+ action:
+ description: Action of the firewall rule
+ returned: success
+ type: str
+ sample: accept
+ protocol:
+ description: Protocol of the firewall rule
+ returned: success
+ type: str
+ sample: tcp
+ start_port:
+ description: Start port of the firewall rule
+ returned: success and protocol is tcp or udp
+ type: int
+ sample: 80
+ end_port:
+ description: End port of the firewall rule
+ returned: success and when port range and protocol is tcp or udp
+ type: int
+ sample: 8080
+ cidr:
+ description: CIDR of the firewall rule (IPv4 or IPv6)
+ returned: success and when port range
+ type: str
+ sample: 0.0.0.0/0
+ group:
+ description: Firewall group the rule is into.
+ returned: success
+ type: str
+ sample: web
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrFirewallRule(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrFirewallRule, self).__init__(module, "vultr_firewall_rule")
+
+ self.returns = {
+ 'rulenumber': dict(key='rule_number'),
+ 'action': dict(),
+ 'protocol': dict(),
+ 'start_port': dict(convert_to='int'),
+ 'end_port': dict(convert_to='int'),
+ 'cidr': dict(),
+ 'group': dict(),
+ }
+ self.firewall_group = None
+
+ def get_firewall_group(self):
+ if self.firewall_group is not None:
+ return self.firewall_group
+
+ firewall_groups = self.api_query(path="/v1/firewall/group_list")
+ if firewall_groups:
+ for firewall_group_id, firewall_group_data in firewall_groups.items():
+ if firewall_group_data.get('description') == self.module.params.get('group'):
+ self.firewall_group = firewall_group_data
+ return self.firewall_group
+ self.fail_json(msg="Firewall group not found: %s" % self.module.params.get('group'))
+
+ def _transform_cidr(self):
+ cidr = self.module.params.get('cidr')
+ ip_version = self.module.params.get('ip_version')
+ if cidr is None:
+ if ip_version == "v6":
+ cidr = "::/0"
+ else:
+ cidr = "0.0.0.0/0"
+ elif cidr.count('/') != 1:
+ self.fail_json(msg="CIDR has an invalid format: %s" % cidr)
+
+ return cidr.split('/')
+
+ def get_firewall_rule(self):
+ ip_version = self.module.params.get('ip_version')
+ firewall_group_id = self.get_firewall_group()['FIREWALLGROUPID']
+
+ firewall_rules = self.api_query(
+ path="/v1/firewall/rule_list"
+ "?FIREWALLGROUPID=%s"
+ "&direction=in"
+ "&ip_type=%s"
+ % (firewall_group_id, ip_version))
+
+ if firewall_rules:
+ subnet, subnet_size = self._transform_cidr()
+
+ for firewall_rule_id, firewall_rule_data in firewall_rules.items():
+ if firewall_rule_data.get('protocol') != self.module.params.get('protocol'):
+ continue
+
+ if ip_version == 'v4' and (firewall_rule_data.get('subnet') or "0.0.0.0") != subnet:
+ continue
+
+ if ip_version == 'v6' and (firewall_rule_data.get('subnet') or "::") != subnet:
+ continue
+
+ if int(firewall_rule_data.get('subnet_size')) != int(subnet_size):
+ continue
+
+ if firewall_rule_data.get('protocol') in ['tcp', 'udp']:
+ rule_port = firewall_rule_data.get('port')
+
+ end_port = self.module.params.get('end_port')
+ start_port = self.module.params.get('start_port')
+
+ # Port range "8000 - 8080" from the API
+ if ' - ' in rule_port:
+ if end_port is None:
+ continue
+
+ port_range = "%s - %s" % (start_port, end_port)
+ if rule_port == port_range:
+ return firewall_rule_data
+
+ # Single port
+ elif int(rule_port) == start_port:
+ return firewall_rule_data
+
+ else:
+ return firewall_rule_data
+
+ return {}
+
+ def present_firewall_rule(self):
+ firewall_rule = self.get_firewall_rule()
+ if not firewall_rule:
+ firewall_rule = self._create_firewall_rule(firewall_rule)
+ return firewall_rule
+
+ def _create_firewall_rule(self, firewall_rule):
+ protocol = self.module.params.get('protocol')
+ if protocol in ['tcp', 'udp']:
+ start_port = self.module.params.get('start_port')
+
+ if start_port is None:
+ self.module.fail_on_missing_params(['start_port'])
+
+ end_port = self.module.params.get('end_port')
+ if end_port is not None:
+
+ if start_port >= end_port:
+ self.module.fail_json(msg="end_port must be higher than start_port")
+
+ port_range = "%s:%s" % (start_port, end_port)
+ else:
+ port_range = start_port
+ else:
+ port_range = None
+
+ self.result['changed'] = True
+
+ subnet, subnet_size = self._transform_cidr()
+
+ data = {
+ 'FIREWALLGROUPID': self.get_firewall_group()['FIREWALLGROUPID'],
+ 'direction': 'in', # currently the only option
+ 'ip_type': self.module.params.get('ip_version'),
+ 'protocol': protocol,
+ 'subnet': subnet,
+ 'subnet_size': subnet_size,
+ 'port': port_range
+ }
+
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/firewall/rule_create",
+ method="POST",
+ data=data
+ )
+ firewall_rule = self.get_firewall_rule()
+ return firewall_rule
+
+ def absent_firewall_rule(self):
+ firewall_rule = self.get_firewall_rule()
+ if firewall_rule:
+ self.result['changed'] = True
+
+ data = {
+ 'FIREWALLGROUPID': self.get_firewall_group()['FIREWALLGROUPID'],
+ 'rulenumber': firewall_rule['rulenumber']
+ }
+
+ self.result['diff']['before'] = firewall_rule
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/firewall/rule_delete",
+ method="POST",
+ data=data
+ )
+ return firewall_rule
+
+ def get_result(self, resource):
+ if resource:
+ if 'port' in resource and resource['protocol'] in ['tcp', 'udp']:
+ if ' - ' in resource['port']:
+ resource['start_port'], resource['end_port'] = resource['port'].split(' - ')
+ else:
+ resource['start_port'] = resource['port']
+ if 'subnet' in resource:
+ resource['cidr'] = "%s/%s" % (resource['subnet'], resource['subnet_size'])
+ resource['group'] = self.get_firewall_group()['description']
+ return super(AnsibleVultrFirewallRule, self).get_result(resource)
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ group=dict(type='str', required=True),
+ start_port=dict(type='int', aliases=['port']),
+ end_port=dict(type='int'),
+ protocol=dict(type='str', choices=['tcp', 'udp', 'gre', 'icmp'], default='tcp'),
+ cidr=dict(type='str',),
+ ip_version=dict(type='str', choices=['v4', 'v6'], default='v4', aliases=['ip_type']),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr_firewall_rule = AnsibleVultrFirewallRule(module)
+ if module.params.get('state') == "absent":
+ firewall_rule = vultr_firewall_rule.absent_firewall_rule()
+ else:
+ firewall_rule = vultr_firewall_rule.present_firewall_rule()
+
+ result = vultr_firewall_rule.get_result(firewall_rule)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network.py
new file mode 100644
index 000000000..3992e3d1e
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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: vultr_network
+short_description: Manages networks on Vultr.
+description:
+ - Manage networks on Vultr. A network cannot be updated. It needs to be deleted and re-created.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+options:
+ name:
+ description:
+ - Name of the network.
+ required: true
+ aliases: [ description, label ]
+ type: str
+ cidr:
+ description:
+ - The CIDR IPv4 network block to be used when attaching servers to this network. Required if I(state=present).
+ type: str
+ region:
+ description:
+ - Region the network is deployed into. Required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the network.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: Ensure a network is present
+ ngine_io.vultr.vultr_network:
+ name: mynet
+ cidr: 192.168.42.0/24
+ region: Amsterdam
+
+- name: Ensure a network is absent
+ ngine_io.vultr.vultr_network:
+ name: mynet
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_network:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the network
+ returned: success
+ type: str
+ sample: "net5b62c6dc63ef5"
+ name:
+ description: Name (label) of the network
+ returned: success
+ type: str
+ sample: "mynetwork"
+ date_created:
+ description: Date when the network was created
+ returned: success
+ type: str
+ sample: "2018-08-02 08:54:52"
+ region:
+ description: Region the network was deployed into
+ returned: success
+ type: str
+ sample: "Amsterdam"
+ v4_subnet:
+ description: IPv4 Network address
+ returned: success
+ type: str
+ sample: "192.168.42.0"
+ v4_subnet_mask:
+ description: Ipv4 Network mask
+ returned: success
+ type: int
+ sample: 24
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrNetwork(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrNetwork, self).__init__(module, "vultr_network")
+
+ self.returns = {
+ 'NETWORKID': dict(key='id'),
+ 'DCID': dict(key='region', transform=self._get_region_name),
+ 'date_created': dict(),
+ 'description': dict(key='name'),
+ 'v4_subnet': dict(),
+ 'v4_subnet_mask': dict(convert_to='int'),
+ }
+
+ def _get_region_name(self, region_id=None):
+ return self.get_region().get('name')
+
+ def get_network(self):
+ networks = self.api_query(path="/v1/network/list")
+ if networks:
+ for id, network in networks.items():
+ if network.get('description') == self.module.params.get('name'):
+ return network
+ return {}
+
+ def present_network(self):
+ network = self.get_network()
+ if not network:
+ network = self._create_network(network)
+ return network
+
+ def _create_network(self, network):
+ self.result['changed'] = True
+ data = {
+ 'description': self.module.params.get('name'),
+ 'DCID': self.get_region()['DCID'],
+ 'v4_subnet': self.module.params.get('cidr').split('/')[0],
+ 'v4_subnet_mask': self.module.params.get('cidr').split('/')[1]
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/network/create",
+ method="POST",
+ data=data
+ )
+ network = self.get_network()
+ return network
+
+ def absent_network(self):
+ network = self.get_network()
+ if network:
+ self.result['changed'] = True
+
+ data = {
+ 'NETWORKID': network['NETWORKID'],
+ }
+
+ self.result['diff']['before'] = network
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/network/destroy",
+ method="POST",
+ data=data
+ )
+ return network
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True, aliases=['description', 'label']),
+ cidr=dict(type='str',),
+ region=dict(type='str',),
+ state=dict(choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[['state', 'present', ['cidr', 'region']]]
+ )
+
+ vultr_network = AnsibleVultrNetwork(module)
+ if module.params.get('state') == "absent":
+ network = vultr_network.absent_network()
+ else:
+ network = vultr_network.present_network()
+
+ result = vultr_network.get_result(network)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network_info.py
new file mode 100644
index 000000000..85f6471ab
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_network_info.py
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_network_info
+short_description: Gather information about the Vultr networks available.
+description:
+ - Gather information about networks available in Vultr.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr networks information
+ ngine_io.vultr.vultr_network_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_network_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_network_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the network
+ returned: success
+ type: str
+ sample: "net5b62c6dc63ef5"
+ name:
+ description: Name (label) of the network
+ returned: success
+ type: str
+ sample: "mynetwork"
+ date_created:
+ description: Date when the network was created
+ returned: success
+ type: str
+ sample: "2018-08-02 08:54:52"
+ region:
+ description: Region the network was deployed into
+ returned: success
+ type: str
+ sample: "Amsterdam"
+ v4_subnet:
+ description: IPv4 Network address
+ returned: success
+ type: str
+ sample: "192.168.42.0"
+ v4_subnet_mask:
+ description: Ipv4 Network mask
+ returned: success
+ type: int
+ sample: 24
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrNetworkInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrNetworkInfo, self).__init__(module, "vultr_network_info")
+
+ self.returns = {
+ 'DCID': dict(key='region', transform=self._get_region_name),
+ 'NETWORKID': dict(key='id'),
+ 'date_created': dict(),
+ 'description': dict(key='name'),
+ 'v4_subnet': dict(),
+ 'v4_subnet_mask': dict(convert_to='int'),
+ }
+
+ def _get_region_name(self, region):
+ return self.query_resource_by_key(
+ key='DCID',
+ value=region,
+ resource='regions',
+ use_cache=True
+ )['name']
+
+ def get_networks(self):
+ return self.api_query(path="/v1/network/list")
+
+
+def parse_network_list(network_list):
+ if isinstance(network_list, list):
+ return []
+
+ return [network for id, network in network_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ network_info = AnsibleVultrNetworkInfo(module)
+ result = network_info.get_result(parse_network_list(network_info.get_networks()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_os_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_os_info.py
new file mode 100644
index 000000000..258b50d52
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_os_info.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2019, 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 = r'''
+---
+module: vultr_os_info
+short_description: Get information about the Vultr OSes available.
+description:
+ - Get infos about OSes available to boot servers.
+version_added: "0.1.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Get Vultr OSes infos
+ ngine_io.vultr.vultr_os_info:
+ register: results
+
+- name: Print the gathered infos
+ debug:
+ var: results.vultr_os_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_os_info:
+ description: Response from Vultr API as list
+ returned: available
+ type: complex
+ contains:
+ arch:
+ description: OS Architecture
+ returned: success
+ type: str
+ sample: x64
+ family:
+ description: OS family
+ returned: success
+ type: str
+ sample: openbsd
+ name:
+ description: OS name
+ returned: success
+ type: str
+ sample: OpenBSD 6 x64
+ windows:
+ description: OS is a MS Windows
+ returned: success
+ type: bool
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrOSInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrOSInfo, self).__init__(module, "vultr_os_info")
+
+ self.returns = {
+ "OSID": dict(key='id', convert_to='int'),
+ "arch": dict(),
+ "family": dict(),
+ "name": dict(),
+ "windows": dict(convert_to='bool')
+ }
+
+ def get_oses(self):
+ return self.api_query(path="/v1/os/list")
+
+
+def parse_oses_list(oses_list):
+ return [os for id, os in oses_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ os_info = AnsibleVultrOSInfo(module)
+ result = os_info.get_result(parse_oses_list(os_info.get_oses()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_baremetal_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_baremetal_info.py
new file mode 100644
index 000000000..040ca2d94
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_baremetal_info.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# (c) 2020, Simon Baerlocher <s.baerlocher@sbaerlocher.ch>
+# 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 = r'''
+---
+module: vultr_plan_baremetal_info
+short_description: Gather information about the Vultr Bare Metal plans available.
+description:
+ - Gather information about Bare Metal plans available to boot servers.
+version_added: "0.3.0"
+author: "Simon Baerlocher (@sbaerlocher)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr Bare Metal plans information
+ ngine_io.vultr.vultr_baremetal_plan_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_baremetal_plan_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_plan_baremetal_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ plan:
+ description: List of the Bare Metal plans available.
+ returned: success
+ type: list
+ sample: [{
+ "available_locations": [
+ 1
+ ],
+ "bandwidth": 40.0,
+ "bandwidth_gb": 40960,
+ "disk": 110,
+ "id": 118,
+ "name": "32768 MB RAM,110 GB SSD,40.00 TB BW",
+ "plan_type": "DEDICATED",
+ "price_per_month": 240.0,
+ "ram": 32768,
+ "vcpu_count": 8,
+ "windows": false
+ }]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrPlanInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrPlanInfo, self).__init__(module, "vultr_plan_baremetal_info")
+
+ self.returns = {
+ "METALPLANID": dict(key='id', convert_to='int'),
+ "available_locations": dict(),
+ "bandwidth_tb": dict(convert_to='int'),
+ "disk": dict(),
+ "name": dict(),
+ "plan_type": dict(),
+ "price_per_month": dict(convert_to='float'),
+ "ram": dict(convert_to='int'),
+ "windows": dict(convert_to='bool'),
+ "cpu_count": dict(convert_to='int'),
+ "cpu_model": dict(),
+ "cpu_thread_count": dict(convert_to='int'),
+ }
+
+ def get_plans_baremetal(self):
+ return self.api_query(path="/v1/plans/list_baremetal")
+
+
+def parse_plans_baremetal_list(plans_baremetal_list):
+ return [plan_baremetal for id, plan_baremetal in plans_baremetal_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ plan_baremetal_info = AnsibleVultrPlanInfo(module)
+ result = plan_baremetal_info.get_result(parse_plans_baremetal_list(plan_baremetal_info.get_plans_baremetal()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_info.py
new file mode 100644
index 000000000..3783ab8b0
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_plan_info.py
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_plan_info
+short_description: Gather information about the Vultr plans available.
+description:
+ - Gather information about plans available to boot servers.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr plans information
+ ngine_io.vultr.vultr_plan_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_plan_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_plan_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ plan:
+ description: List of the plans available.
+ returned: success
+ type: list
+ sample: [{
+ "available_locations": [
+ 1
+ ],
+ "bandwidth": 40.0,
+ "bandwidth_gb": 40960,
+ "disk": 110,
+ "id": 118,
+ "name": "32768 MB RAM,110 GB SSD,40.00 TB BW",
+ "plan_type": "DEDICATED",
+ "price_per_month": 240.0,
+ "ram": 32768,
+ "vcpu_count": 8,
+ "windows": false
+ }]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrPlanInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrPlanInfo, self).__init__(module, "vultr_plan_info")
+
+ self.returns = {
+ "VPSPLANID": dict(key='id', convert_to='int'),
+ "available_locations": dict(),
+ "bandwidth": dict(convert_to='float'),
+ "bandwidth_gb": dict(convert_to='int'),
+ "disk": dict(convert_to='int'),
+ "name": dict(),
+ "plan_type": dict(),
+ "price_per_month": dict(convert_to='float'),
+ "ram": dict(convert_to='int'),
+ "vcpu_count": dict(convert_to='int'),
+ "windows": dict(convert_to='bool')
+ }
+
+ def get_plans(self):
+ return self.api_query(path="/v1/plans/list")
+
+
+def parse_plans_list(plans_list):
+ return [plan for id, plan in plans_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ plan_info = AnsibleVultrPlanInfo(module)
+ result = plan_info.get_result(parse_plans_list(plan_info.get_plans()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_region_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_region_info.py
new file mode 100644
index 000000000..2080d2d58
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_region_info.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_region_info
+short_description: Gather information about the Vultr regions available.
+description:
+ - Gather information about regions available to boot servers.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr regions information
+ ngine_io.vultr.vultr_region_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_region_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_region_info:
+ description: Response from Vultr API
+ returned: success
+ type: list
+ sample: [
+ {
+ "block_storage": false,
+ "continent": "Europe",
+ "country": "GB",
+ "ddos_protection": true,
+ "id": 8,
+ "name": "London",
+ "regioncode": "LHR",
+ "state": ""
+ }
+ ]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrRegionInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrRegionInfo, self).__init__(module, "vultr_region_info")
+
+ self.returns = {
+ "DCID": dict(key='id', convert_to='int'),
+ "block_storage": dict(convert_to='bool'),
+ "continent": dict(),
+ "country": dict(),
+ "ddos_protection": dict(convert_to='bool'),
+ "name": dict(),
+ "regioncode": dict(),
+ "state": dict()
+ }
+
+ def get_regions(self):
+ return self.api_query(path="/v1/regions/list")
+
+
+def parse_regions_list(regions_list):
+ return [region for id, region in regions_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ region_info = AnsibleVultrRegionInfo(module)
+ result = region_info.get_result(parse_regions_list(region_info.get_regions()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server.py
new file mode 100644
index 000000000..b423766e2
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server.py
@@ -0,0 +1,933 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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: vultr_server
+short_description: Manages virtual servers on Vultr.
+description:
+ - Deploy, start, stop, update, restart, reinstall servers.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the server.
+ required: true
+ aliases: [ label ]
+ type: str
+ hostname:
+ description:
+ - The hostname to assign to this server.
+ type: str
+ os:
+ description:
+ - The operating system name or ID.
+ - Required if the server does not yet exist and is not restoring from a snapshot.
+ type: str
+ snapshot:
+ description:
+ - Name or ID of the snapshot to restore the server from.
+ type: str
+ firewall_group:
+ description:
+ - The firewall group description or ID to assign this server to.
+ type: str
+ plan:
+ description:
+ - Plan name or ID to use for the server.
+ - Required if the server does not yet exist.
+ type: str
+ force:
+ description:
+ - 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.
+ - Only considered on creation.
+ type: bool
+ default: false
+ private_network_enabled:
+ description:
+ - Whether to enable private networking or not.
+ type: bool
+ auto_backup_enabled:
+ description:
+ - Whether to enable automatic backups or not.
+ type: bool
+ ipv6_enabled:
+ description:
+ - Whether to enable IPv6 or not.
+ type: bool
+ tag:
+ description:
+ - Tag for the server.
+ type: str
+ user_data:
+ description:
+ - User data to be passed to the server.
+ type: str
+ startup_script:
+ description:
+ - 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 key names or IDs passed to the server on creation.
+ aliases: [ ssh_key ]
+ type: list
+ elements: str
+ reserved_ip_v4:
+ description:
+ - IP address of the floating IP to use as the main IP of this server.
+ - Only considered on creation.
+ type: str
+ region:
+ description:
+ - Region name or ID the server is deployed into.
+ - Required if the server does not yet exist.
+ type: str
+ state:
+ description:
+ - State of the server.
+ default: present
+ choices: [ present, absent, restarted, reinstalled, started, stopped ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: create server
+ ngine_io.vultr.vultr_server:
+ name: "{{ vultr_server_name }}"
+ os: CentOS 7 x64
+ plan: 1024 MB RAM,25 GB SSD,1.00 TB BW
+ ssh_keys:
+ - my_key
+ - your_key
+ region: Amsterdam
+ state: present
+
+- name: ensure a server is present and started
+ ngine_io.vultr.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 provisioned using IDs
+ ngine_io.vultr.vultr_server:
+ name: "{{ vultr_server_name }}"
+ os: "167"
+ plan: "201"
+ region: "7"
+ state: stopped
+
+- name: ensure an existing server is stopped
+ ngine_io.vultr.vultr_server:
+ name: "{{ vultr_server_name }}"
+ state: stopped
+
+- name: ensure an existing server is started
+ ngine_io.vultr.vultr_server:
+ name: "{{ vultr_server_name }}"
+ state: started
+
+- name: ensure a server is absent
+ ngine_io.vultr.vultr_server:
+ name: "{{ vultr_server_name }}"
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_server:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the server
+ returned: success
+ type: str
+ sample: 10194376
+ name:
+ description: Name (label) of the server
+ returned: success
+ type: str
+ sample: "ansible-test-vm"
+ plan:
+ description: Plan used for the server
+ returned: success
+ type: str
+ sample: "1024 MB RAM,25 GB SSD,1.00 TB BW"
+ allowed_bandwidth_gb:
+ description: Allowed bandwidth to use in GB
+ returned: success
+ type: float
+ sample: 1000.5
+ auto_backup_enabled:
+ description: Whether automatic backups are enabled
+ returned: success
+ type: bool
+ sample: false
+ cost_per_month:
+ description: Cost per month for the server
+ returned: success
+ type: float
+ sample: 5.00
+ current_bandwidth_gb:
+ description: Current bandwidth used for the server
+ returned: success
+ type: int
+ sample: 0
+ date_created:
+ description: Date when the server was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ default_password:
+ description: Password to login as root into the server
+ returned: success
+ type: str
+ sample: "!p3EWYJm$qDWYaFr"
+ disk:
+ description: Information about the disk
+ returned: success
+ type: str
+ sample: "Virtual 25 GB"
+ v4_gateway:
+ description: IPv4 gateway
+ returned: success
+ type: str
+ sample: "45.32.232.1"
+ internal_ip:
+ description: Internal IP
+ returned: success
+ type: str
+ sample: ""
+ kvm_url:
+ description: URL to the VNC
+ returned: success
+ type: str
+ sample: "https://my.vultr.com/subs/vps/novnc/api.php?data=xyz"
+ region:
+ description: Region the server was deployed into
+ returned: success
+ type: str
+ sample: "Amsterdam"
+ v4_main_ip:
+ description: Main IPv4
+ returned: success
+ type: str
+ sample: "45.32.233.154"
+ v4_netmask:
+ description: Netmask IPv4
+ returned: success
+ type: str
+ sample: "255.255.254.0"
+ os:
+ description: Operating system used for the server
+ returned: success
+ type: str
+ sample: "CentOS 6 x64"
+ firewall_group:
+ description: Firewall group the server is assigned to
+ returned: success and available
+ type: str
+ sample: "CentOS 6 x64"
+ pending_charges:
+ description: Pending charges
+ returned: success
+ type: float
+ sample: 0.01
+ power_status:
+ description: Power status of the server
+ returned: success
+ type: str
+ sample: "running"
+ ram:
+ description: Information about the RAM size
+ returned: success
+ type: str
+ sample: "1024 MB"
+ server_state:
+ description: State about the server
+ returned: success
+ type: str
+ sample: "ok"
+ status:
+ description: Status about the deployment of the server
+ returned: success
+ type: str
+ sample: "active"
+ tag:
+ description: TBD
+ returned: success
+ type: str
+ sample: ""
+ v6_main_ip:
+ description: Main IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_network:
+ description: Network IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_network_size:
+ description: Network size IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_networks:
+ description: Networks IPv6
+ returned: success
+ type: list
+ sample: []
+ vcpu_count:
+ description: Virtual CPU count
+ returned: success
+ type: int
+ sample: 1
+'''
+
+import time
+import base64
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_text, to_bytes
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrServer(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrServer, self).__init__(module, "vultr_server")
+
+ self.server = None
+ self.returns = {
+ 'SUBID': dict(key='id'),
+ 'label': dict(key='name'),
+ 'date_created': dict(),
+ 'allowed_bandwidth_gb': dict(convert_to='float'),
+ 'auto_backups': dict(key='auto_backup_enabled', convert_to='bool'),
+ 'current_bandwidth_gb': dict(),
+ 'kvm_url': dict(),
+ 'default_password': dict(),
+ 'internal_ip': dict(),
+ 'disk': dict(),
+ 'cost_per_month': dict(convert_to='float'),
+ 'location': dict(key='region'),
+ 'main_ip': dict(key='v4_main_ip'),
+ 'network_v4': dict(key='v4_network'),
+ 'gateway_v4': dict(key='v4_gateway'),
+ 'os': dict(),
+ 'pending_charges': dict(convert_to='float'),
+ 'power_status': dict(),
+ 'ram': dict(),
+ 'plan': dict(),
+ 'server_state': dict(),
+ 'status': dict(),
+ 'firewall_group': dict(),
+ 'tag': dict(),
+ 'v6_main_ip': dict(),
+ 'v6_network': dict(),
+ 'v6_network_size': dict(),
+ 'v6_networks': dict(),
+ 'vcpu_count': dict(convert_to='int'),
+ }
+ self.server_power_state = None
+
+ def get_startup_script(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('startup_script'),
+ resource='startupscript',
+ )
+
+ def get_os(self):
+ if self.module.params.get('snapshot'):
+ os_name = 'Snapshot'
+ else:
+ os_name = self.module.params.get('os')
+
+ return self.query_resource_by_key(
+ key='name',
+ value=os_name,
+ resource='os',
+ use_cache=True,
+ id_key='OSID',
+ )
+
+ def get_snapshot(self):
+ return self.query_resource_by_key(
+ key='description',
+ value=self.module.params.get('snapshot'),
+ resource='snapshot',
+ id_key='SNAPSHOTID',
+ )
+
+ def get_ssh_keys(self):
+ ssh_key_names = self.module.params.get('ssh_keys')
+ if not ssh_key_names:
+ return []
+
+ ssh_keys = []
+ for ssh_key_name in ssh_key_names:
+ ssh_key = self.query_resource_by_key(
+ key='name',
+ value=ssh_key_name,
+ resource='sshkey',
+ use_cache=True,
+ id_key='SSHKEYID',
+ )
+ if ssh_key:
+ ssh_keys.append(ssh_key)
+ return ssh_keys
+
+ def get_region(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('region'),
+ resource='regions',
+ use_cache=True,
+ id_key='DCID',
+ )
+
+ def get_firewall_group(self):
+ return self.query_resource_by_key(
+ key='description',
+ value=self.module.params.get('firewall_group'),
+ resource='firewall',
+ query_by='group_list',
+ id_key='FIREWALLGROUPID'
+ )
+
+ def get_user_data(self):
+ user_data = self.module.params.get('user_data')
+ if user_data is not None:
+ user_data = to_text(base64.b64encode(to_bytes(user_data)))
+ return user_data
+
+ def get_server_user_data(self, server):
+ if not server or not server.get('SUBID'):
+ return None
+
+ user_data = self.api_query(path="/v1/server/get_user_data?SUBID=%s" % server.get('SUBID'))
+ return user_data.get('userdata')
+
+ def get_server(self, refresh=False):
+ if self.server is None or refresh:
+ self.server = None
+ server_list = self.api_query(path="/v1/server/list")
+ if server_list:
+ for server_id, server_data in server_list.items():
+ if server_data.get('label') == self.module.params.get('name'):
+ self.server = server_data
+
+ plan = self.query_resource_by_key(
+ key='VPSPLANID',
+ value=server_data['VPSPLANID'],
+ resource='plans',
+ use_cache=True
+ )
+ self.server['plan'] = plan.get('name')
+
+ os = self.query_resource_by_key(
+ key='OSID',
+ value=int(server_data['OSID']),
+ resource='os',
+ use_cache=True
+ )
+ self.server['os'] = os.get('name')
+
+ fwg_id = server_data.get('FIREWALLGROUPID')
+ fw = self.query_resource_by_key(
+ key='FIREWALLGROUPID',
+ value=server_data.get('FIREWALLGROUPID') if fwg_id and fwg_id != "0" else None,
+ resource='firewall',
+ query_by='group_list',
+ use_cache=True
+ )
+ self.server['firewall_group'] = fw.get('description')
+ return self.server
+
+ def present_server(self, start_server=True):
+ server = self.get_server()
+ if not server:
+ server = self._create_server(server=server)
+ else:
+ server = self._update_server(server=server, start_server=start_server)
+ return server
+
+ def _create_server(self, server=None):
+ required_params = [
+ 'os',
+ 'plan',
+ 'region',
+ ]
+
+ snapshot_restore = self.module.params.get('snapshot') is not None
+ if snapshot_restore:
+ required_params.remove('os')
+
+ self.module.fail_on_missing_params(required_params=required_params)
+
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ data = {
+ 'DCID': self.get_region().get('DCID'),
+ 'VPSPLANID': self.get_plan().get('VPSPLANID'),
+ 'FIREWALLGROUPID': self.get_firewall_group().get('FIREWALLGROUPID'),
+ 'OSID': self.get_os().get('OSID'),
+ 'SNAPSHOTID': self.get_snapshot().get('SNAPSHOTID'),
+ 'label': self.module.params.get('name'),
+ 'hostname': self.module.params.get('hostname'),
+ 'SSHKEYID': ','.join([ssh_key['SSHKEYID'] for ssh_key in self.get_ssh_keys()]),
+ 'enable_ipv6': self.get_yes_or_no('ipv6_enabled'),
+ 'enable_private_network': self.get_yes_or_no('private_network_enabled'),
+ 'auto_backups': self.get_yes_or_no('auto_backup_enabled'),
+ 'notify_activate': self.get_yes_or_no('notify_activate'),
+ 'tag': self.module.params.get('tag'),
+ 'reserved_ip_v4': self.module.params.get('reserved_ip_v4'),
+ 'userdata': self.get_user_data(),
+ 'SCRIPTID': self.get_startup_script().get('SCRIPTID'),
+ }
+ self.api_query(
+ path="/v1/server/create",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(key='status', state='active')
+ server = self._wait_for_state(state='running', timeout=3600 if snapshot_restore else 60)
+ return server
+
+ def _update_auto_backups_setting(self, server, start_server):
+ auto_backup_enabled_changed = self.switch_enable_disable(server, 'auto_backup_enabled', 'auto_backups')
+
+ if auto_backup_enabled_changed:
+ if auto_backup_enabled_changed == "enable" and server['auto_backups'] == 'disable':
+ self.module.warn("Backups are disabled. Once disabled, backups can only be enabled again by customer support")
+ else:
+ server, warned = self._handle_power_status_for_update(server, start_server)
+ if not warned:
+ self.result['changed'] = True
+ self.result['diff']['before']['auto_backup_enabled'] = server.get('auto_backups')
+ self.result['diff']['after']['auto_backup_enabled'] = self.get_yes_or_no('auto_backup_enabled')
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/backup_%s" % auto_backup_enabled_changed,
+ method="POST",
+ data=data
+ )
+ return server
+
+ def _update_ipv6_setting(self, server, start_server):
+ ipv6_enabled_changed = self.switch_enable_disable(server, 'ipv6_enabled', 'v6_main_ip')
+
+ if ipv6_enabled_changed:
+ if ipv6_enabled_changed == "disable":
+ self.module.warn("The Vultr API does not allow to disable IPv6")
+ else:
+ server, warned = self._handle_power_status_for_update(server, start_server)
+ if not warned:
+ self.result['changed'] = True
+ self.result['diff']['before']['ipv6_enabled'] = False
+ self.result['diff']['after']['ipv6_enabled'] = True
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/ipv6_%s" % ipv6_enabled_changed,
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(key='v6_main_ip')
+ return server
+
+ def _update_private_network_setting(self, server, start_server):
+ private_network_enabled_changed = self.switch_enable_disable(server, 'private_network_enabled', 'internal_ip')
+ if private_network_enabled_changed:
+ if private_network_enabled_changed == "disable":
+ self.module.warn("The Vultr API does not allow to disable private network")
+ else:
+ server, warned = self._handle_power_status_for_update(server, start_server)
+ if not warned:
+ self.result['changed'] = True
+ self.result['diff']['before']['private_network_enabled'] = False
+ self.result['diff']['after']['private_network_enabled'] = True
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/private_network_%s" % private_network_enabled_changed,
+ method="POST",
+ data=data
+ )
+ return server
+
+ def _update_plan_setting(self, server, start_server):
+ # Verify the exising plan is not discontined by Vultr and therefore won't be found by the API
+ server_plan = self.get_plan(plan=server.get('VPSPLANID'), optional=True)
+ if not server_plan:
+ plan = self.get_plan(optional=True)
+ if not plan:
+ self.module.warn("The plan used to create the server is not longer available as well as the desired plan. Assuming same plan, keeping as is.")
+ return server
+ else:
+ plan = self.get_plan()
+
+ plan_changed = True if plan and plan['VPSPLANID'] != server.get('VPSPLANID') else False
+ if plan_changed:
+ server, warned = self._handle_power_status_for_update(server, start_server)
+ if not warned:
+ self.result['changed'] = True
+ self.result['diff']['before']['plan'] = server.get('plan')
+ self.result['diff']['after']['plan'] = plan['name']
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'VPSPLANID': plan['VPSPLANID'],
+ }
+ self.api_query(
+ path="/v1/server/upgrade_plan",
+ method="POST",
+ data=data
+ )
+ return server
+
+ def _handle_power_status_for_update(self, server, start_server):
+ # Remember the power state before we handle any action
+ if self.server_power_state is None:
+ self.server_power_state = server['power_status']
+
+ # A stopped server can be updated
+ if self.server_power_state == "stopped":
+ return server, False
+
+ # A running server must be forced to update unless the wanted state is stopped
+ elif self.module.params.get('force') or not start_server:
+ warned = False
+ if not self.module.check_mode:
+ # Some update APIs would restart the VM, we handle the restart manually
+ # by stopping the server and start it at the end of the changes
+ server = self.stop_server(skip_results=True)
+
+ # Warn the user that a running server won't get changed
+ else:
+ warned = True
+ self.module.warn("Some changes won't be applied to running instances. " +
+ "Use force=true to allow the instance %s to be stopped/started." % server['label'])
+
+ return server, warned
+
+ def _update_server(self, server=None, start_server=True):
+ # Wait for server to unlock if restoring
+ if server.get('os').strip() == 'Snapshot':
+ server = self._wait_for_state(key='server_status', state='ok', timeout=3600)
+
+ # Update auto backups settings, stops server
+ server = self._update_auto_backups_setting(server=server, start_server=start_server)
+
+ # Update IPv6 settings, stops server
+ server = self._update_ipv6_setting(server=server, start_server=start_server)
+
+ # Update private network settings, stops server
+ server = self._update_private_network_setting(server=server, start_server=start_server)
+
+ # Update plan settings, stops server
+ server = self._update_plan_setting(server=server, start_server=start_server)
+
+ # User data
+ user_data = self.get_user_data()
+ server_user_data = self.get_server_user_data(server=server)
+ if user_data is not None and user_data != server_user_data:
+ self.result['changed'] = True
+ self.result['diff']['before']['user_data'] = server_user_data
+ self.result['diff']['after']['user_data'] = user_data
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'userdata': user_data,
+ }
+ self.api_query(
+ path="/v1/server/set_user_data",
+ method="POST",
+ data=data
+ )
+
+ # Tags
+ tag = self.module.params.get('tag')
+ if tag is not None and tag != server.get('tag'):
+ self.result['changed'] = True
+ self.result['diff']['before']['tag'] = server.get('tag')
+ self.result['diff']['after']['tag'] = tag
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'tag': tag,
+ }
+ self.api_query(
+ path="/v1/server/tag_set",
+ method="POST",
+ data=data
+ )
+
+ # Firewall group
+ firewall_group = self.get_firewall_group()
+ if firewall_group and firewall_group.get('description') != server.get('firewall_group'):
+ self.result['changed'] = True
+ self.result['diff']['before']['firewall_group'] = server.get('firewall_group')
+ self.result['diff']['after']['firewall_group'] = firewall_group.get('description')
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'FIREWALLGROUPID': firewall_group.get('FIREWALLGROUPID'),
+ }
+ self.api_query(
+ path="/v1/server/firewall_group_set",
+ method="POST",
+ data=data
+ )
+ # Start server again if it was running before the changes
+ if not self.module.check_mode:
+ if self.server_power_state in ['starting', 'running'] and start_server:
+ server = self.start_server(skip_results=True)
+
+ server = self._wait_for_state(key='status', state='active')
+ return server
+
+ def absent_server(self):
+ server = self.get_server()
+ if server:
+ self.result['changed'] = True
+ self.result['diff']['before']['id'] = server['SUBID']
+ self.result['diff']['after']['id'] = ""
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/destroy",
+ method="POST",
+ data=data
+ )
+ for s in range(0, 60):
+ if server is not None:
+ break
+ time.sleep(2)
+ server = self.get_server(refresh=True)
+ else:
+ self.fail_json(msg="Wait for server '%s' to get deleted timed out" % server['label'])
+ return server
+
+ def restart_server(self):
+ self.result['changed'] = True
+ server = self.get_server()
+ if server:
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/reboot",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(state='running')
+ return server
+
+ def reinstall_server(self):
+ self.result['changed'] = True
+ server = self.get_server()
+ if server:
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/reinstall",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(state='running')
+ return server
+
+ def _wait_for_state(self, key='power_status', state=None, timeout=60):
+ time.sleep(1)
+ server = self.get_server(refresh=True)
+ for s in range(0, timeout):
+ # Check for Truely if wanted state is None
+ if state is None and server.get(key):
+ break
+ elif server.get(key) == state:
+ break
+ time.sleep(2)
+ server = self.get_server(refresh=True)
+
+ # Timed out
+ else:
+ if state is None:
+ msg = "Wait for '%s' timed out" % key
+ else:
+ msg = "Wait for '%s' to get into state '%s' timed out" % (key, state)
+ self.fail_json(msg=msg)
+ return server
+
+ def start_server(self, skip_results=False):
+ server = self.get_server()
+ if server:
+ if server['power_status'] == 'starting':
+ server = self._wait_for_state(state='running')
+
+ elif server['power_status'] != 'running':
+ if not skip_results:
+ self.result['changed'] = True
+ self.result['diff']['before']['power_status'] = server['power_status']
+ self.result['diff']['after']['power_status'] = "running"
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/server/start",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(state='running')
+ return server
+
+ def stop_server(self, skip_results=False):
+ server = self.get_server()
+ if server and server['power_status'] != "stopped":
+ if not skip_results:
+ self.result['changed'] = True
+ self.result['diff']['before']['power_status'] = server['power_status']
+ self.result['diff']['after']['power_status'] = "stopped"
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ }
+ self.api_query(
+ path="/v1/server/halt",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(state='stopped')
+ return server
+
+
+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'),
+ 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'),
+ ssh_keys=dict(type='list', elements='str', aliases=['ssh_key'], no_log=False),
+ region=dict(type='str'),
+ state=dict(choices=['present', 'absent', 'restarted', 'reinstalled', 'started', 'stopped'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr_server = AnsibleVultrServer(module)
+ if module.params.get('state') == "absent":
+ server = vultr_server.absent_server()
+ else:
+ if module.params.get('state') == "started":
+ server = vultr_server.present_server()
+ server = vultr_server.start_server()
+ elif module.params.get('state') == "stopped":
+ server = vultr_server.present_server(start_server=False)
+ server = vultr_server.stop_server()
+ elif module.params.get('state') == "restarted":
+ server = vultr_server.present_server()
+ server = vultr_server.restart_server()
+ elif module.params.get('state') == "reinstalled":
+ server = vultr_server.reinstall_server()
+ else:
+ server = vultr_server.present_server()
+
+ result = vultr_server.get_result(server)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_baremetal.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_baremetal.py
new file mode 100644
index 000000000..279f3d14b
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_baremetal.py
@@ -0,0 +1,548 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# (c) 2019, Nate River <vitikc@gmail.com>
+# (c) 2020, Simon Baerlocher <s.baerlocher@sbaerlocher.ch>
+# 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 = r'''
+---
+module: vultr_server_baremetal
+short_description: Manages baremetal servers on Vultr.
+description:
+ - Deploy and destroy servers.
+version_added: "0.3.0"
+author:
+ - "Nate River (@vitikc)"
+ - "Simon Baerlocher (@sbaerlocher)"
+options:
+ name:
+ description:
+ - Name of the server.
+ required: true
+ aliases: [ label ]
+ type: str
+ hostname:
+ description:
+ - The hostname to assign to this server.
+ type: str
+ os:
+ description:
+ - The operating system name or ID.
+ - Required if the server does not yet exist and is not restoring from a snapshot.
+ type: str
+ plan:
+ description:
+ - Plan name or ID to use for the server.
+ - Required if the server does not yet exist.
+ type: str
+ notify_activate:
+ description:
+ - Whether to send an activation email when the server is ready or not.
+ - Only considered on creation.
+ type: bool
+ default: false
+ ipv6_enabled:
+ description:
+ - Whether to enable IPv6 or not.
+ type: bool
+ tag:
+ description:
+ - Tag for the server.
+ type: str
+ user_data:
+ description:
+ - User data to be passed to the server.
+ type: str
+ startup_script:
+ description:
+ - 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 key names or IDs passed to the server on creation.
+ aliases: [ ssh_key ]
+ type: list
+ elements: str
+ reserved_ip_v4:
+ description:
+ - IP address of the floating IP to use as the main IP of this server.
+ - Only considered on creation.
+ type: str
+ region:
+ description:
+ - Region name or ID the server is deployed into.
+ - Required if the server does not yet exist.
+ type: str
+ state:
+ description:
+ - State of the server.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+'''
+
+EXAMPLES = r'''
+- name: create server
+ ngine_io.vultr.vultr_server_baremetal:
+ name: "{{ vultr_server_baremetal_name }}"
+ os: Debian 9 x64 (stretch)
+ plan: 32768 MB RAM,2x 240 GB SSD,5.00 TB BW
+ region: Amsterdam
+
+- name: ensure a server is absent
+ ngine_io.vultr.vultr_server_baremetal:
+ name: "{{ vultr_server_baremetal_name }}"
+ state: absent
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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_endpoint:
+ description: Endpoint used for the API requests
+ returned: success
+ type: str
+ sample: "https://api.vultr.com"
+vultr_server_baremetal:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the server
+ returned: success
+ type: str
+ sample: 900000
+ name:
+ description: Name (label) of the server
+ returned: success
+ type: str
+ sample: "ansible-test-baremetal"
+ plan:
+ description: Plan used for the server
+ returned: success
+ type: str
+ sample: "32768 MB RAM,2x 240 GB SSD,5.00 TB BW"
+ allowed_bandwidth_gb:
+ description: Allowed bandwidth to use in GB
+ returned: success
+ type: float
+ sample: 1000.5
+ cost_per_month:
+ description: Cost per month for the server
+ returned: success
+ type: float
+ sample: 120.00
+ current_bandwidth_gb:
+ description: Current bandwidth used for the server
+ returned: success
+ type: int
+ sample: 0
+ date_created:
+ description: Date when the server was created
+ returned: success
+ type: str
+ sample: "2017-04-12 18:45:41"
+ default_password:
+ description: Password to login as root into the server
+ returned: success
+ type: str
+ sample: "ab81u!ryranq"
+ disk:
+ description: Information about the disk
+ returned: success
+ type: str
+ sample: "SSD 250 GB"
+ v4_gateway:
+ description: IPv4 gateway
+ returned: success
+ type: str
+ sample: "203.0.113.1"
+ internal_ip:
+ description: Internal IP
+ returned: success
+ type: str
+ sample: ""
+ region:
+ description: Region the server was deployed into
+ returned: success
+ type: str
+ sample: "Amsterdam"
+ v4_main_ip:
+ description: Main IPv4
+ returned: success
+ type: str
+ sample: "203.0.113.10"
+ v4_netmask:
+ description: Netmask IPv4
+ returned: success
+ type: str
+ sample: "255.255.255.0"
+ os:
+ description: Operating system used for the server
+ returned: success
+ type: str
+ sample: "Debian 9 x64"
+ pending_charges:
+ description: Pending charges
+ returned: success
+ type: float
+ sample: 0.18
+ ram:
+ description: Information about the RAM size
+ returned: success
+ type: str
+ sample: "32768 MB"
+ status:
+ description: Status about the deployment of the server
+ returned: success
+ type: str
+ sample: "active"
+ tag:
+ description: Server tag
+ returned: success
+ type: str
+ sample: "my tag"
+ v6_main_ip:
+ description: Main IPv6
+ returned: success
+ type: str
+ sample: "2001:DB8:9000::100"
+ v6_network:
+ description: Network IPv6
+ returned: success
+ type: str
+ sample: "2001:DB8:9000::"
+ v6_network_size:
+ description: Network size IPv6
+ returned: success
+ type: int
+ sample: 64
+ v6_networks:
+ description: Networks IPv6
+ returned: success
+ type: list
+ sample: []
+'''
+
+import time
+import base64
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_text, to_bytes
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrServerBareMetal(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrServerBareMetal, self).__init__(module, "vultr_server_baremetal")
+
+ self.server = None
+ self.returns = {
+ 'SUBID': dict(key='id'),
+ 'label': dict(key='name'),
+ 'date_created': dict(),
+ 'allowed_bandwidth_gb': dict(convert_to='float'),
+ 'current_bandwidth_gb': dict(),
+ 'default_password': dict(),
+ 'internal_ip': dict(),
+ 'disk': dict(),
+ 'cost_per_month': dict(convert_to='float'),
+ 'location': dict(key='region'),
+ 'main_ip': dict(key='v4_main_ip'),
+ 'network_v4': dict(key='v4_network'),
+ 'gateway_v4': dict(key='v4_gateway'),
+ 'os': dict(),
+ 'pending_charges': dict(convert_to='float'),
+ 'ram': dict(),
+ 'plan': dict(),
+ 'status': dict(),
+ 'tag': dict(),
+ 'v6_main_ip': dict(),
+ 'v6_network': dict(),
+ 'v6_network_size': dict(),
+ 'v6_networks': dict(),
+ }
+ self.server_power_state = None
+
+ def get_startup_script(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('startup_script'),
+ resource='startupscript',
+ )
+
+ def get_os(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('os'),
+ resource='os',
+ use_cache=True
+ )
+
+ def get_ssh_keys(self):
+ ssh_key_names = self.module.params.get('ssh_keys')
+ if not ssh_key_names:
+ return []
+
+ ssh_keys = []
+ for ssh_key_name in ssh_key_names:
+ ssh_key = self.query_resource_by_key(
+ key='name',
+ value=ssh_key_name,
+ resource='sshkey',
+ use_cache=True
+ )
+ if ssh_key:
+ ssh_keys.append(ssh_key)
+ return ssh_keys
+
+ def get_region(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('region'),
+ resource='regions',
+ use_cache=True
+ )
+
+ def get_plan(self):
+ return self.query_resource_by_key(
+ key='name',
+ value=self.module.params.get('plan'),
+ resource='plans',
+ query_by='list_baremetal',
+ use_cache=True
+ )
+
+ def get_user_data(self):
+ user_data = self.module.params.get('user_data')
+ if user_data is not None:
+ user_data = to_text(base64.b64encode(to_bytes(user_data)))
+ return user_data
+
+ def get_server_user_data(self, server):
+ if not server or not server.get('SUBID'):
+ return None
+
+ user_data = self.api_query(path="/v1/baremetal/get_user_data?SUBID=%s" % server.get('SUBID'))
+ return user_data.get('userdata')
+
+ def get_server(self, refresh=False):
+ if self.server is None or refresh:
+ self.server = None
+ server_list = self.api_query(path="/v1/baremetal/list")
+ if server_list:
+ for server_id, server_data in server_list.items():
+ if server_data.get('label') == self.module.params.get('name'):
+ self.server = server_data
+
+ plan = self.query_resource_by_key(
+ key='METALPLANID',
+ value=server_data['METALPLANID'],
+ resource='plans',
+ query_by='list_baremetal',
+ use_cache=True
+ )
+ self.server['plan'] = plan.get('name')
+
+ os = self.query_resource_by_key(
+ key='OSID',
+ value=int(server_data['OSID']),
+ resource='os',
+ use_cache=True
+ )
+ self.server['os'] = os.get('name')
+ return self.server
+
+ def _wait_for_state(self, key='status', state=None):
+ time.sleep(1)
+ server = self.get_server(refresh=True)
+ for s in range(0, 500):
+ if state is None and server.get(key):
+ break
+ elif server.get(key) == state:
+ break
+ time.sleep(2)
+ server = self.get_server(refresh=True)
+
+ # Timed out
+ else:
+ if state is None:
+ msg = "Wait for '%s' timed out" % key
+ else:
+ msg = "Wait for '%s' to get into state '%s' timed out" % (key, state)
+ self.fail_json(msg=msg)
+ return server
+
+ def present_server(self, start_server=True):
+ server = self.get_server()
+ if not server:
+ server = self._create_server(server=server)
+ else:
+ server = self._update_server(server=server, start_server=start_server)
+ return server
+
+ def _create_server(self, server=None):
+ required_params = [
+ 'os',
+ 'plan',
+ 'region',
+ ]
+ self.module.fail_on_missing_params(required_params=required_params)
+
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ data = {
+ 'DCID': self.get_region().get('DCID'),
+ 'METALPLANID': self.get_plan().get('METALPLANID'),
+ 'OSID': self.get_os().get('OSID'),
+ 'label': self.module.params.get('name'),
+ 'hostname': self.module.params.get('hostname'),
+ 'SSHKEYID': ','.join([ssh_key['SSHKEYID'] for ssh_key in self.get_ssh_keys()]),
+ 'enable_ipv6': self.get_yes_or_no('ipv6_enabled'),
+ 'notify_activate': self.get_yes_or_no('notify_activate'),
+ 'tag': self.module.params.get('tag'),
+ 'reserved_ip_v4': self.module.params.get('reserved_ip_v4'),
+ 'user_data': self.get_user_data(),
+ 'SCRIPTID': self.get_startup_script().get('SCRIPTID'),
+ }
+ self.api_query(
+ path="/v1/baremetal/create",
+ method="POST",
+ data=data
+ )
+ server = self._wait_for_state(key='status', state='active')
+ return server
+
+ def _update_server(self, server=None, start_server=True):
+
+ # Update plan settings
+ # server = self._update_plan_setting(server=server, start_server=start_server)
+
+ # User data
+ user_data = self.get_user_data()
+ server_user_data = self.get_server_user_data(server=server)
+ if user_data is not None and user_data != server_user_data:
+ self.result['changed'] = True
+ self.result['diff']['before']['user_data'] = server_user_data
+ self.result['diff']['after']['user_data'] = user_data
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'userdata': user_data,
+ }
+ self.api_query(
+ path="/v1/baremetal/set_user_data",
+ method="POST",
+ data=data
+ )
+
+ # Tags
+ tag = self.module.params.get('tag')
+ if tag is not None and tag != server.get('tag'):
+ self.result['changed'] = True
+ self.result['diff']['before']['tag'] = server.get('tag')
+ self.result['diff']['after']['tag'] = tag
+
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID'],
+ 'tag': tag,
+ }
+ self.api_query(
+ path="/v1/baremetal/tag_set",
+ method="POST",
+ data=data
+ )
+ return server
+
+ def absent_server(self):
+ server = self.get_server()
+ if server:
+ self.result['changed'] = True
+ self.result['diff']['before']['id'] = server['SUBID']
+ self.result['diff']['after']['id'] = ""
+ if not self.module.check_mode:
+ data = {
+ 'SUBID': server['SUBID']
+ }
+ self.api_query(
+ path="/v1/baremetal/destroy",
+ method="POST",
+ data=data
+ )
+ for s in range(0, 60):
+ if server is not None:
+ break
+ time.sleep(2)
+ server = self.get_server(refresh=True)
+ else:
+ self.fail_json(msg="Wait for server '%s' to get deleted timed out" % server['label'])
+ return server
+
+
+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',),
+ plan=dict(type='str',),
+ notify_activate=dict(type='bool', default=False),
+ ipv6_enabled=dict(type='bool'),
+ tag=dict(type='str',),
+ reserved_ip_v4=dict(type='str',),
+ startup_script=dict(type='str',),
+ user_data=dict(type='str',),
+ ssh_keys=dict(type='list', elements='str', aliases=['ssh_key'], no_log=False),
+ region=dict(type='str',),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr_server_baremetal = AnsibleVultrServerBareMetal(module)
+ if module.params.get('state') == "absent":
+ server = vultr_server_baremetal.absent_server()
+ else:
+ server = vultr_server_baremetal.present_server()
+
+ result = vultr_server_baremetal.get_result(server)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_info.py
new file mode 100644
index 000000000..a2608ac14
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_server_info.py
@@ -0,0 +1,300 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_server_info
+short_description: Gather information about the Vultr servers available.
+description:
+ - Gather information about servers available.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr servers information
+ ngine_io.vultr.vultr_server_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_server_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_server_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the server
+ returned: success
+ type: str
+ sample: 10194376
+ name:
+ description: Name (label) of the server
+ returned: success
+ type: str
+ sample: "ansible-test-vm"
+ plan:
+ description: Plan used for the server
+ returned: success
+ type: str
+ sample: "1024 MB RAM,25 GB SSD,1.00 TB BW"
+ allowed_bandwidth_gb:
+ description: Allowed bandwidth to use in GB
+ returned: success
+ type: float
+ sample: 1000.5
+ auto_backup_enabled:
+ description: Whether automatic backups are enabled
+ returned: success
+ type: bool
+ sample: false
+ cost_per_month:
+ description: Cost per month for the server
+ returned: success
+ type: float
+ sample: 5.00
+ current_bandwidth_gb:
+ description: Current bandwidth used for the server
+ returned: success
+ type: int
+ sample: 0
+ date_created:
+ description: Date when the server was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ default_password:
+ description: Password to login as root into the server
+ returned: success
+ type: str
+ sample: "!p3EWYJm$qDWYaFr"
+ disk:
+ description: Information about the disk
+ returned: success
+ type: str
+ sample: "Virtual 25 GB"
+ v4_gateway:
+ description: IPv4 gateway
+ returned: success
+ type: str
+ sample: "45.32.232.1"
+ internal_ip:
+ description: Internal IP
+ returned: success
+ type: str
+ sample: ""
+ kvm_url:
+ description: URL to the VNC
+ returned: success
+ type: str
+ sample: "https://my.vultr.com/subs/vps/novnc/api.php?data=xyz"
+ region:
+ description: Region the server was deployed into
+ returned: success
+ type: str
+ sample: "Amsterdam"
+ v4_main_ip:
+ description: Main IPv4
+ returned: success
+ type: str
+ sample: "45.32.233.154"
+ v4_netmask:
+ description: Netmask IPv4
+ returned: success
+ type: str
+ sample: "255.255.254.0"
+ os:
+ description: Operating system used for the server
+ returned: success
+ type: str
+ sample: "CentOS 6 x64"
+ firewall_group:
+ description: Firewall group the server is assigned to
+ returned: success and available
+ type: str
+ sample: "CentOS 6 x64"
+ pending_charges:
+ description: Pending charges
+ returned: success
+ type: float
+ sample: 0.01
+ power_status:
+ description: Power status of the server
+ returned: success
+ type: str
+ sample: "running"
+ ram:
+ description: Information about the RAM size
+ returned: success
+ type: str
+ sample: "1024 MB"
+ server_state:
+ description: State about the server
+ returned: success
+ type: str
+ sample: "ok"
+ status:
+ description: Status about the deployment of the server
+ returned: success
+ type: str
+ sample: "active"
+ tag:
+ description: TBD
+ returned: success
+ type: str
+ sample: ""
+ v6_main_ip:
+ description: Main IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_network:
+ description: Network IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_network_size:
+ description: Network size IPv6
+ returned: success
+ type: str
+ sample: ""
+ v6_networks:
+ description: Networks IPv6
+ returned: success
+ type: list
+ sample: []
+ vcpu_count:
+ description: Virtual CPU count
+ returned: success
+ type: int
+ sample: 1
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrServerInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrServerInfo, self).__init__(module, "vultr_server_info")
+
+ self.returns = {
+ "APPID": dict(key='application', convert_to='int', transform=self._get_application_name),
+ "FIREWALLGROUPID": dict(key='firewallgroup', transform=self._get_firewallgroup_name),
+ "SUBID": dict(key='id', convert_to='int'),
+ "VPSPLANID": dict(key='plan', convert_to='int', transform=self._get_plan_name),
+ "allowed_bandwidth_gb": dict(convert_to='float'),
+ 'auto_backups': dict(key='auto_backup_enabled', convert_to='bool'),
+ "cost_per_month": dict(convert_to='float'),
+ "current_bandwidth_gb": dict(convert_to='float'),
+ "date_created": dict(),
+ "default_password": dict(),
+ "disk": dict(),
+ "gateway_v4": dict(key='v4_gateway'),
+ "internal_ip": dict(),
+ "kvm_url": dict(),
+ "label": dict(key='name'),
+ "location": dict(key='region'),
+ "main_ip": dict(key='v4_main_ip'),
+ "netmask_v4": dict(key='v4_netmask'),
+ "os": dict(),
+ "pending_charges": dict(convert_to='float'),
+ "power_status": dict(),
+ "ram": dict(),
+ "server_state": dict(),
+ "status": dict(),
+ "tag": dict(),
+ "v6_main_ip": dict(),
+ "v6_network": dict(),
+ "v6_network_size": dict(),
+ "v6_networks": dict(),
+ "vcpu_count": dict(convert_to='int'),
+ }
+
+ def _get_application_name(self, application):
+ if application == 0:
+ return None
+
+ return self.get_application(application, 'APPID').get('name')
+
+ def _get_firewallgroup_name(self, firewallgroup):
+ if firewallgroup == 0:
+ return None
+
+ return self.get_firewallgroup(firewallgroup, 'FIREWALLGROUPID').get('description')
+
+ def _get_plan_name(self, plan):
+ return self.get_plan(plan, 'VPSPLANID', optional=True).get('name') or 'N/A'
+
+ def get_servers(self):
+ return self.api_query(path="/v1/server/list")
+
+
+def parse_servers_list(servers_list):
+ return [server for id, server in servers_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ server_info = AnsibleVultrServerInfo(module)
+ result = server_info.get_result(parse_servers_list(server_info.get_servers()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key.py
new file mode 100644
index 000000000..11e648ce6
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key.py
@@ -0,0 +1,236 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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: vultr_ssh_key
+short_description: Manages ssh keys on Vultr.
+description:
+ - Create, update and remove ssh keys.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the ssh key.
+ required: true
+ type: str
+ ssh_key:
+ description:
+ - SSH public key.
+ - Required if C(state=present).
+ type: str
+ state:
+ description:
+ - State of the ssh key.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = '''
+- name: ensure an SSH key is present
+ ngine_io.vultr.vultr_ssh_key:
+ name: my ssh key
+ ssh_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+
+- name: ensure an SSH key is absent
+ ngine_io.vultr.vultr_ssh_key:
+ name: my ssh key
+ state: absent
+'''
+
+RETURN = '''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_ssh_key:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the ssh key
+ returned: success
+ type: str
+ sample: 5904bc6ed9234
+ name:
+ description: Name of the ssh key
+ returned: success
+ type: str
+ sample: my ssh key
+ date_created:
+ description: Date the ssh key was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ ssh_key:
+ description: SSH public key
+ returned: success
+ type: str
+ sample: "ssh-rsa AA... someother@example.com"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrSshKey(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrSshKey, self).__init__(module, "vultr_ssh_key")
+
+ self.returns = {
+ 'SSHKEYID': dict(key='id'),
+ 'name': dict(),
+ 'ssh_key': dict(),
+ 'date_created': dict(),
+ }
+
+ def get_ssh_key(self):
+ ssh_keys = self.api_query(path="/v1/sshkey/list")
+ if ssh_keys:
+ for ssh_key_id, ssh_key_data in ssh_keys.items():
+ if ssh_key_data.get('name') == self.module.params.get('name'):
+ return ssh_key_data
+ return {}
+
+ def present_ssh_key(self):
+ ssh_key = self.get_ssh_key()
+ if not ssh_key:
+ ssh_key = self._create_ssh_key(ssh_key)
+ else:
+ ssh_key = self._update_ssh_key(ssh_key)
+ return ssh_key
+
+ def _create_ssh_key(self, ssh_key):
+ self.result['changed'] = True
+ data = {
+ 'name': self.module.params.get('name'),
+ 'ssh_key': self.module.params.get('ssh_key'),
+ }
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/sshkey/create",
+ method="POST",
+ data=data
+ )
+ ssh_key = self.get_ssh_key()
+ return ssh_key
+
+ def _update_ssh_key(self, ssh_key):
+ param_ssh_key = self.module.params.get('ssh_key')
+ if param_ssh_key != ssh_key['ssh_key']:
+ self.result['changed'] = True
+
+ data = {
+ 'SSHKEYID': ssh_key['SSHKEYID'],
+ 'ssh_key': param_ssh_key,
+ }
+
+ self.result['diff']['before'] = ssh_key
+ self.result['diff']['after'] = data
+ self.result['diff']['after'].update({'date_created': ssh_key['date_created']})
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/sshkey/update",
+ method="POST",
+ data=data
+ )
+ ssh_key = self.get_ssh_key()
+ return ssh_key
+
+ def absent_ssh_key(self):
+ ssh_key = self.get_ssh_key()
+ if ssh_key:
+ self.result['changed'] = True
+
+ data = {
+ 'SSHKEYID': ssh_key['SSHKEYID'],
+ }
+
+ self.result['diff']['before'] = ssh_key
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/sshkey/destroy",
+ method="POST",
+ data=data
+ )
+ return ssh_key
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True),
+ ssh_key=dict(type='str', no_log=False),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ('state', 'present', ['ssh_key']),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr_ssh_key = AnsibleVultrSshKey(module)
+ if module.params.get('state') == "absent":
+ ssh_key = vultr_ssh_key.absent_ssh_key()
+ else:
+ ssh_key = vultr_ssh_key.present_ssh_key()
+
+ result = vultr_ssh_key.get_result(ssh_key)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key_info.py
new file mode 100644
index 000000000..51b2960bb
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_ssh_key_info.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2019, 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 = r'''
+---
+module: vultr_ssh_key_info
+short_description: Get information about the Vultr SSH keys available.
+description:
+ - Get infos about SSH keys available.
+version_added: "0.1.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Get Vultr SSH keys infos
+ ngine_io.vultr.vultr_ssh_key_info:
+ register: result
+
+- name: Print the infos
+ debug:
+ var: result.vultr_ssh_key_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_ssh_key_info:
+ description: Response from Vultr API as list
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the ssh key
+ returned: success
+ type: str
+ sample: 5904bc6ed9234
+ name:
+ description: Name of the ssh key
+ returned: success
+ type: str
+ sample: my ssh key
+ date_created:
+ description: Date the ssh key was created
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ ssh_key:
+ description: SSH public key
+ returned: success
+ type: str
+ sample: "ssh-rsa AA... someother@example.com"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrSSHKeyInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrSSHKeyInfo, self).__init__(module, "vultr_ssh_key_info")
+
+ self.returns = {
+ 'SSHKEYID': dict(key='id'),
+ 'name': dict(),
+ 'ssh_key': dict(),
+ 'date_created': dict(),
+ }
+
+ def get_sshkeys(self):
+ return self.api_query(path="/v1/sshkey/list")
+
+
+def parse_keys_list(keys_list):
+ if not keys_list:
+ return []
+
+ return [key for id, key in keys_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ sshkey_info = AnsibleVultrSSHKeyInfo(module)
+ result = sshkey_info.get_result(parse_keys_list(sshkey_info.get_sshkeys()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script.py
new file mode 100644
index 000000000..dfa58af77
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script.py
@@ -0,0 +1,265 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, 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 = r'''
+---
+module: vultr_startup_script
+short_description: Manages startup scripts on Vultr.
+description:
+ - Create, update and remove startup scripts.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - The script name.
+ required: true
+ type: str
+ script_type:
+ description:
+ - The script type, can not be changed once created.
+ default: boot
+ choices: [ boot, pxe ]
+ aliases: [ type ]
+ type: str
+ script:
+ description:
+ - The script source code.
+ - Required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the script.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: ensure a pxe script exists, source from a file
+ ngine_io.vultr.vultr_startup_script:
+ name: my_web_script
+ script_type: pxe
+ script: "{{ lookup('file', 'path/to/script') }}"
+
+- name: ensure a boot script exists
+ ngine_io.vultr.vultr_startup_script:
+ name: vultr_startup_script
+ script: "#!/bin/bash\necho Hello World > /root/hello"
+
+- name: ensure a script is absent
+ ngine_io.vultr.vultr_startup_script:
+ name: my_web_script
+ state: absent
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_startup_script:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the startup script.
+ returned: success
+ type: str
+ sample: 249395
+ name:
+ description: Name of the startup script.
+ returned: success
+ type: str
+ sample: my startup script
+ script:
+ description: The source code of the startup script.
+ returned: success
+ type: str
+ sample: "#!/bin/bash\necho Hello World > /root/hello"
+ script_type:
+ description: The type of the startup script.
+ returned: success
+ type: str
+ sample: pxe
+ date_created:
+ description: Date the startup script was created.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ date_modified:
+ description: Date the startup script was modified.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrStartupScript(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrStartupScript, self).__init__(module, "vultr_startup_script")
+
+ self.returns = {
+ 'SCRIPTID': dict(key='id'),
+ 'type': dict(key='script_type'),
+ 'name': dict(),
+ 'script': dict(),
+ 'date_created': dict(),
+ 'date_modified': dict(),
+ }
+
+ def get_script(self):
+ scripts = self.api_query(path="/v1/startupscript/list")
+ name = self.module.params.get('name')
+ if scripts:
+ for script_id, script_data in scripts.items():
+ if script_data.get('name') == name:
+ return script_data
+ return {}
+
+ def present_script(self):
+ script = self.get_script()
+ if not script:
+ script = self._create_script(script)
+ else:
+ script = self._update_script(script)
+ return script
+
+ def _create_script(self, script):
+ self.result['changed'] = True
+
+ data = {
+ 'name': self.module.params.get('name'),
+ 'script': self.module.params.get('script'),
+ 'type': self.module.params.get('script_type'),
+ }
+
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/startupscript/create",
+ method="POST",
+ data=data
+ )
+ script = self.get_script()
+ return script
+
+ def _update_script(self, script):
+ if script['script'] != self.module.params.get('script'):
+ self.result['changed'] = True
+
+ data = {
+ 'SCRIPTID': script['SCRIPTID'],
+ 'script': self.module.params.get('script'),
+ }
+
+ self.result['diff']['before'] = script
+ self.result['diff']['after'] = script.copy()
+ self.result['diff']['after'].update(data)
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/startupscript/update",
+ method="POST",
+ data=data
+ )
+ script = self.get_script()
+ return script
+
+ def absent_script(self):
+ script = self.get_script()
+ if script:
+ self.result['changed'] = True
+
+ data = {
+ 'SCRIPTID': script['SCRIPTID'],
+ }
+
+ self.result['diff']['before'] = script
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/startupscript/destroy",
+ method="POST",
+ data=data
+ )
+ return script
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True),
+ script=dict(type='str',),
+ script_type=dict(type='str', default='boot', choices=['boot', 'pxe'], aliases=['type']),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ('state', 'present', ['script']),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr_script = AnsibleVultrStartupScript(module)
+ if module.params.get('state') == "absent":
+ script = vultr_script.absent_script()
+ else:
+ script = vultr_script.present_script()
+
+ result = vultr_script.get_result(script)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script_info.py
new file mode 100644
index 000000000..262954ad3
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_startup_script_info.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# 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 = r'''
+---
+module: vultr_startup_script_info
+short_description: Gather information about the Vultr startup scripts available.
+description:
+ - Gather information about vultr_startup_scripts available.
+version_added: "0.1.0"
+author: "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Gather Vultr startup scripts information
+ ngine_io.vultr.vultr_startup_script_info:
+ register: result
+
+- name: Print the gathered information
+ debug:
+ var: result.vultr_startup_script_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_startup_script_info:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the startup script.
+ returned: success
+ type: str
+ sample: 249395
+ name:
+ description: Name of the startup script.
+ returned: success
+ type: str
+ sample: my startup script
+ script:
+ description: The source code of the startup script.
+ returned: success
+ type: str
+ sample: "#!/bin/bash\necho Hello World > /root/hello"
+ type:
+ description: The type of the startup script.
+ returned: success
+ type: str
+ sample: pxe
+ date_created:
+ description: Date the startup script was created.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+ date_modified:
+ description: Date the startup script was modified.
+ returned: success
+ type: str
+ sample: "2017-08-26 12:47:48"
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrStartupScriptInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrStartupScriptInfo, self).__init__(module, "vultr_startup_script_info")
+
+ self.returns = {
+ "SCRIPTID": dict(key='id', convert_to='int'),
+ "date_created": dict(),
+ "date_modified": dict(),
+ "name": dict(),
+ "script": dict(),
+ "type": dict(),
+ }
+
+ def get_startupscripts(self):
+ return self.api_query(path="/v1/startupscript/list")
+
+
+def parse_startupscript_list(startupscipts_list):
+ if not startupscipts_list:
+ return []
+
+ return [startupscript for id, startupscript in startupscipts_list.items()]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ startupscript_info = AnsibleVultrStartupScriptInfo(module)
+ result = startupscript_info.get_result(parse_startupscript_list(startupscript_info.get_startupscripts()))
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user.py
new file mode 100644
index 000000000..53ebfeacc
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user.py
@@ -0,0 +1,326 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, 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 = r'''
+---
+module: vultr_user
+short_description: Manages users on Vultr.
+description:
+ - Create, update and remove users.
+version_added: "0.1.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the user
+ required: true
+ type: str
+ email:
+ description:
+ - Email of the user.
+ - Required if C(state=present).
+ type: str
+ password:
+ description:
+ - Password of the user.
+ - Only considered while creating a user or when C(force=yes).
+ type: str
+ force:
+ description:
+ - Password will only be changed with enforcement.
+ default: no
+ type: bool
+ api_enabled:
+ description:
+ - Whether the API is enabled or not.
+ default: yes
+ type: bool
+ acls:
+ description:
+ - List of ACLs this users should have, see U(https://www.vultr.com/api/#user_user_list).
+ - Required if C(state=present).
+ - One or more of the choices list, some depend on each other.
+ choices:
+ - manage_users
+ - subscriptions
+ - provisioning
+ - billing
+ - support
+ - abuse
+ - dns
+ - upgrade
+ aliases: [ acl ]
+ type: list
+ elements: str
+ state:
+ description:
+ - State of the user.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Ensure a user exists
+ ngine_io.vultr.vultr_user:
+ name: john
+ email: john.doe@example.com
+ password: s3cr3t
+ acls:
+ - upgrade
+ - dns
+ - manage_users
+ - subscriptions
+ - upgrade
+
+- name: Remove a user
+ ngine_io.vultr.vultr_user:
+ name: john
+ state: absent
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_user:
+ description: Response from Vultr API
+ returned: success
+ type: complex
+ contains:
+ id:
+ description: ID of the user.
+ returned: success
+ type: str
+ sample: 5904bc6ed9234
+ api_key:
+ description: API key of the user.
+ returned: only after resource was created
+ type: str
+ sample: 567E6K567E6K567E6K567E6K567E6K
+ name:
+ description: Name of the user.
+ returned: success
+ type: str
+ sample: john
+ email:
+ description: Email of the user.
+ returned: success
+ type: str
+ sample: "john@example.com"
+ api_enabled:
+ description: Whether the API is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ acls:
+ description: List of ACLs of the user.
+ returned: success
+ type: list
+ sample: [manage_users, support, upgrade]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+ACLS = [
+ 'manage_users',
+ 'subscriptions',
+ 'provisioning',
+ 'billing',
+ 'support',
+ 'abuse',
+ 'dns',
+ 'upgrade',
+]
+
+
+class AnsibleVultrUser(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrUser, self).__init__(module, "vultr_user")
+
+ self.returns = {
+ 'USERID': dict(key='id'),
+ 'name': dict(),
+ 'email': dict(),
+ 'api_enabled': dict(convert_to='bool'),
+ 'acls': dict(),
+ 'api_key': dict()
+ }
+
+ def _common_args(self):
+ return {
+ 'name': self.module.params.get('name'),
+ 'email': self.module.params.get('email'),
+ 'acls': self.module.params.get('acls'),
+ 'password': self.module.params.get('password'),
+ 'api_enabled': self.get_yes_or_no('api_enabled'),
+ }
+
+ def get_user(self):
+ users = self.api_query(path="/v1/user/list")
+ for user in users or []:
+ if user.get('name') == self.module.params.get('name'):
+ return user
+ return {}
+
+ def present_user(self):
+ user = self.get_user()
+ if not user:
+ user = self._create_user(user)
+ else:
+ user = self._update_user(user)
+ return user
+
+ def _has_changed(self, user, data):
+ for k, v in data.items():
+ if k not in user:
+ continue
+ elif isinstance(v, list):
+ for i in v:
+ if i not in user[k]:
+ return True
+ elif data[k] != user[k]:
+ return True
+ return False
+
+ def _create_user(self, user):
+ self.module.fail_on_missing_params(required_params=['password'])
+
+ self.result['changed'] = True
+
+ data = self._common_args()
+ self.result['diff']['before'] = {}
+ self.result['diff']['after'] = data
+
+ if not self.module.check_mode:
+ user = self.api_query(
+ path="/v1/user/create",
+ method="POST",
+ data=data
+ )
+ user.update(self.get_user())
+ return user
+
+ def _update_user(self, user):
+ data = self._common_args()
+ data.update({
+ 'USERID': user['USERID'],
+ })
+
+ force = self.module.params.get('force')
+ if not force:
+ del data['password']
+
+ if force or self._has_changed(user=user, data=data):
+ self.result['changed'] = True
+
+ self.result['diff']['before'] = user
+ self.result['diff']['after'] = user.copy()
+ self.result['diff']['after'].update(data)
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/user/update",
+ method="POST",
+ data=data
+ )
+ user = self.get_user()
+ return user
+
+ def absent_user(self):
+ user = self.get_user()
+ if user:
+ self.result['changed'] = True
+
+ data = {
+ 'USERID': user['USERID'],
+ }
+
+ self.result['diff']['before'] = user
+ self.result['diff']['after'] = {}
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="/v1/user/delete",
+ method="POST",
+ data=data
+ )
+ return user
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(dict(
+ name=dict(type='str', required=True),
+ email=dict(type='str',),
+ password=dict(type='str', no_log=True),
+ force=dict(type='bool', default=False),
+ api_enabled=dict(type='bool', default=True),
+ acls=dict(type='list', elements='str', choices=ACLS, aliases=['acl']),
+ state=dict(type='str', choices=['present', 'absent'], default='present'),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ('state', 'present', ['email', 'acls']),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr_user = AnsibleVultrUser(module)
+ if module.params.get('state') == "absent":
+ user = vultr_user.absent_user()
+ else:
+ user = vultr_user.present_user()
+
+ result = vultr_user.get_result(user)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user_info.py b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user_info.py
new file mode 100644
index 000000000..f07d0efff
--- /dev/null
+++ b/ansible_collections/ngine_io/vultr/plugins/modules/vultr_user_info.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2019, 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 = r'''
+---
+module: vultr_user_info
+short_description: Get information about the Vultr user available.
+version_added: "0.1.0"
+description:
+ - Get infos about users available in Vultr.
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+- ngine_io.vultr.vultr
+
+'''
+
+EXAMPLES = r'''
+- name: Get Vultr user infos
+ ngine_io.vultr.vultr_user_info:
+ register: result
+
+- name: Print the infos
+ debug:
+ var: result.vultr_user_info
+'''
+
+RETURN = r'''
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification
+ returned: success
+ type: complex
+ 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"
+vultr_user_info:
+ description: Response from Vultr API as list
+ returned: available
+ type: complex
+ contains:
+ id:
+ description: ID of the user.
+ returned: success
+ type: str
+ sample: 5904bc6ed9234
+ api_key:
+ description: API key of the user.
+ returned: only after resource was created
+ type: str
+ sample: 567E6K567E6K567E6K567E6K567E6K
+ name:
+ description: Name of the user.
+ returned: success
+ type: str
+ sample: john
+ email:
+ description: Email of the user.
+ returned: success
+ type: str
+ sample: "john@example.com"
+ api_enabled:
+ description: Whether the API is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ acls:
+ description: List of ACLs of the user.
+ returned: success
+ type: list
+ sample: [ manage_users, support, upgrade ]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..module_utils.vultr import (
+ Vultr,
+ vultr_argument_spec,
+)
+
+
+class AnsibleVultrUserInfo(Vultr):
+
+ def __init__(self, module):
+ super(AnsibleVultrUserInfo, self).__init__(module, "vultr_user_info")
+
+ self.returns = {
+ "USERID": dict(key='id'),
+ "acls": dict(),
+ "api_enabled": dict(),
+ "email": dict(),
+ "name": dict()
+ }
+
+ def get_regions(self):
+ return self.api_query(path="/v1/user/list")
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ user_info = AnsibleVultrUserInfo(module)
+ result = user_info.get_result(user_info.get_regions())
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()