1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Remove DigitalOcean modules (moved to community.digitalocean) (#622)

* Remove DigitalOcean modules.

* Remove inventory scripts.
This commit is contained in:
Felix Fontein 2020-07-07 16:41:16 +02:00 committed by GitHub
parent 25aec0d712
commit c1b5b51366
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
84 changed files with 154 additions and 5011 deletions

28
.github/BOTMETA.yml vendored
View file

@ -196,33 +196,6 @@ files:
authors: krsacme authors: krsacme
$modules/cloud/centurylink/: $modules/cloud/centurylink/:
authors: clc-runner authors: clc-runner
$modules/cloud/digital_ocean/digital_ocean.py:
authors: zbal
$modules/cloud/digital_ocean/:
authors: Akasurde
maintainers: $team_digital_ocean
keywords:
- digital ocean
- droplet
$modules/cloud/digital_ocean/digital_ocean_firewall_facts.py:
authors: BondAnthony
maintainers: mgregson
$modules/cloud/digital_ocean/digital_ocean_floating_ip_facts.py:
authors: pmarques
$modules/cloud/digital_ocean/digital_ocean_sshkey_facts.py:
authors: pmarques
$modules/cloud/digital_ocean/digital_ocean_block_storage.py:
authors: harneksidhu
$modules/cloud/digital_ocean/digital_ocean_domain.py:
authors: mgregson
maintainers: BondAnthony
$modules/cloud/digital_ocean/digital_ocean_droplet.py:
authors: gurch101
$modules/cloud/digital_ocean/digital_ocean_firewall_info.py:
authors: BondAnthony
maintainers: mgregson
$modules/cloud/digital_ocean/digital_ocean_tag.py:
authors: kontrafiktion
$modules/cloud/dimensiondata/dimensiondata_network.py: $modules/cloud/dimensiondata/dimensiondata_network.py:
authors: aimonb authors: aimonb
maintainers: tintoy maintainers: tintoy
@ -1355,7 +1328,6 @@ macros:
team_aix: MorrisA bcoca d-little flynn1973 gforster kairoaraujo marvin-sinister mator molekuul ramooncamacho wtcross team_aix: MorrisA bcoca d-little flynn1973 gforster kairoaraujo marvin-sinister mator molekuul ramooncamacho wtcross
team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo
team_cyberark_conjur: jvanderhoof ryanprior team_cyberark_conjur: jvanderhoof ryanprior
team_digital_ocean: BondAnthony mgregson
team_docker: DBendit WojciechowskiPiotr akshay196 danihodovic dariko felixfontein jwitko kassiansun tbouvet team_docker: DBendit WojciechowskiPiotr akshay196 danihodovic dariko felixfontein jwitko kassiansun tbouvet
team_e_spirit: MatrixCrawler getjack team_e_spirit: MatrixCrawler getjack
team_extreme: LindsayHill bigmstone ujwalkomarla team_extreme: LindsayHill bigmstone ujwalkomarla

View file

@ -0,0 +1,2 @@
removed_features:
- "digital_ocean_* - all DigitalOcean modules have been moved to the ``community.digitalocean`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/622)."

View file

@ -71,59 +71,173 @@ plugin_routing:
digital_ocean: digital_ocean:
deprecation: deprecation:
removal_version: 2.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean
digital_ocean_account_facts: digital_ocean_account_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_account_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_account_facts
digital_ocean_account_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_account_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_account_info
digital_ocean_block_storage:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_block_storage module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_block_storage
digital_ocean_certificate:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_certificate module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_certificate
digital_ocean_certificate_facts: digital_ocean_certificate_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_certificate_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_certificate_facts
digital_ocean_certificate_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_certificate_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_certificate_info
digital_ocean_domain:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_domain module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_domain
digital_ocean_domain_facts: digital_ocean_domain_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_domain_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_domain_facts
digital_ocean_domain_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_domain_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_domain_info
digital_ocean_droplet:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_droplet module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_droplet
digital_ocean_firewall_facts: digital_ocean_firewall_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_firewall_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_firewall_facts
digital_ocean_firewall_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_firewall_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_firewall_info
digital_ocean_floating_ip:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_floating_ip module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_floating_ip
digital_ocean_floating_ip_facts: digital_ocean_floating_ip_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_floating_ip_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_floating_ip_facts
digital_ocean_floating_ip_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_floating_ip_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_floating_ip_info
digital_ocean_image_facts: digital_ocean_image_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_image_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_image_facts
digital_ocean_image_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_image_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_image_info
digital_ocean_load_balancer_facts: digital_ocean_load_balancer_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_load_balancer_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_load_balancer_facts
digital_ocean_load_balancer_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_load_balancer_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_load_balancer_info
digital_ocean_region_facts: digital_ocean_region_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_region_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_region_facts
digital_ocean_region_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_region_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_region_info
digital_ocean_size_facts: digital_ocean_size_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_size_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_size_facts
digital_ocean_size_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_size_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_size_info
digital_ocean_snapshot_facts: digital_ocean_snapshot_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_snapshot_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_snapshot_facts
digital_ocean_snapshot_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_snapshot_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_snapshot_info
digital_ocean_sshkey:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_sshkey module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_sshkey
digital_ocean_sshkey_facts: digital_ocean_sshkey_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_sshkey_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_sshkey_facts
digital_ocean_sshkey_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_sshkey_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_sshkey_info
digital_ocean_tag:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_tag module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_tag
digital_ocean_tag_facts: digital_ocean_tag_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_tag_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_tag_facts
digital_ocean_tag_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_tag_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_tag_info
digital_ocean_volume_facts: digital_ocean_volume_facts:
deprecation: deprecation:
removal_version: 3.0.0 removal_version: 2.0.0
warning_text: see plugin documentation for details warning_text: The digital_ocean_volume_facts module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_volume_facts
digital_ocean_volume_info:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean_volume_info module has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean_volume_info
docker_image_facts: docker_image_facts:
deprecation: deprecation:
removal_version: 2.0.0 removal_version: 2.0.0
@ -508,8 +622,19 @@ plugin_routing:
removal_version: 1.0.0 removal_version: 1.0.0
warning_text: The logicmonitor_facts module is no longer maintained and the API used has been disabled in 2017. warning_text: The logicmonitor_facts module is no longer maintained and the API used has been disabled in 2017.
doc_fragments: doc_fragments:
digital_ocean:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean docs_fragment has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean
proxysql: proxysql:
deprecation: deprecation:
removal_version: 2.0.0 removal_version: 2.0.0
warning_text: The proxysql docs_fragment has been moved to the community.proxysql collection. warning_text: The proxysql docs_fragment has been moved to the community.proxysql collection.
redirect: community.proxysql.proxysql redirect: community.proxysql.proxysql
module_utils:
digital_ocean:
deprecation:
removal_version: 2.0.0
warning_text: The digital_ocean module_utils has been moved to the community.digitalocean collection.
redirect: community.digitalocean.digital_ocean

View file

@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde (akasurde@redhat.com)
# 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):
# Parameters for DigitalOcean modules
DOCUMENTATION = r'''
options:
oauth_token:
description:
- DigitalOcean OAuth token.
- "There are several other environment variables which can be used to provide this value."
- "i.e., - 'DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN' and 'OAUTH_TOKEN'"
type: str
aliases: [ api_token ]
timeout:
description:
- The timeout in seconds used for polling DigitalOcean's API.
type: int
default: 30
validate_certs:
description:
- If set to C(no), the SSL certificates will not be validated.
- This should only set to C(no) used on personally controlled sites using self-signed certificates.
type: bool
default: yes
'''

View file

@ -1,131 +0,0 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Copyright (c), Ansible Project 2017
# 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 json
import os
from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback
class Response(object):
def __init__(self, resp, info):
self.body = None
if resp:
self.body = resp.read()
self.info = info
@property
def json(self):
if not self.body:
if "body" in self.info:
return json.loads(to_text(self.info["body"]))
return None
try:
return json.loads(to_text(self.body))
except ValueError:
return None
@property
def status_code(self):
return self.info["status"]
class DigitalOceanHelper:
def __init__(self, module):
self.module = module
self.baseurl = 'https://api.digitalocean.com/v2'
self.timeout = module.params.get('timeout', 30)
self.oauth_token = module.params.get('oauth_token')
self.headers = {'Authorization': 'Bearer {0}'.format(self.oauth_token),
'Content-type': 'application/json'}
# Check if api_token is valid or not
response = self.get('account')
if response.status_code == 401:
self.module.fail_json(msg='Failed to login using API token, please verify validity of API token.')
def _url_builder(self, path):
if path[0] == '/':
path = path[1:]
return '%s/%s' % (self.baseurl, path)
def send(self, method, path, data=None):
url = self._url_builder(path)
data = self.module.jsonify(data)
resp, info = fetch_url(self.module, url, data=data, headers=self.headers, method=method, timeout=self.timeout)
return Response(resp, info)
def get(self, path, data=None):
return self.send('GET', path, data)
def put(self, path, data=None):
return self.send('PUT', path, data)
def post(self, path, data=None):
return self.send('POST', path, data)
def delete(self, path, data=None):
return self.send('DELETE', path, data)
@staticmethod
def digital_ocean_argument_spec():
return dict(
validate_certs=dict(type='bool', required=False, default=True),
oauth_token=dict(
no_log=True,
# Support environment variable for DigitalOcean OAuth Token
fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN', 'OAUTH_TOKEN']),
required=False,
aliases=['api_token'],
),
timeout=dict(type='int', default=30),
)
def get_paginated_data(self, base_url=None, data_key_name=None, data_per_page=40, expected_status_code=200):
"""
Function to get all paginated data from given URL
Args:
base_url: Base URL to get data from
data_key_name: Name of data key value
data_per_page: Number results per page (Default: 40)
expected_status_code: Expected returned code from DigitalOcean (Default: 200)
Returns: List of data
"""
page = 1
has_next = True
ret_data = []
status_code = None
response = None
while has_next or status_code != expected_status_code:
required_url = "{0}page={1}&per_page={2}".format(base_url, page, data_per_page)
response = self.get(required_url)
status_code = response.status_code
# stop if any error during pagination
if status_code != expected_status_code:
break
page += 1
ret_data.extend(response.json[data_key_name])
has_next = "pages" in response.json["links"] and "next" in response.json["links"]["pages"]
if status_code != expected_status_code:
msg = "Failed to fetch %s from %s" % (data_key_name, base_url)
if response:
msg += " due to error : %s" % response.json['message']
self.module.fail_json(msg=msg)
return ret_data

View file

@ -1,475 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Project
# 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: digital_ocean
short_description: Create/delete a droplet/SSH_key in DigitalOcean
deprecated:
removed_in: 2.0.0 # was Ansible 2.12
why: Updated module to remove external dependency with increased functionality.
alternative: Use M(community.general.digital_ocean_droplet) instead.
description:
- Create/delete a droplet in DigitalOcean and optionally wait for it to be 'running', or deploy an SSH key.
author: "Vincent Viallet (@zbal)"
options:
command:
description:
- Which target you want to operate on.
default: droplet
choices: ['droplet', 'ssh']
state:
description:
- Indicate desired state of the target.
default: present
choices: ['present', 'active', 'absent', 'deleted']
api_token:
description:
- DigitalOcean api token.
id:
description:
- Numeric, the droplet id you want to operate on.
aliases: ['droplet_id']
name:
description:
- String, this is the name of the droplet - must be formatted by hostname rules, or the name of a SSH key.
unique_name:
description:
- Bool, require unique hostnames. By default, DigitalOcean allows multiple hosts with the same name. Setting this to "yes" allows only one host
per name. Useful for idempotence.
type: bool
default: 'no'
size_id:
description:
- This is the slug of the size you would like the droplet created with.
image_id:
description:
- This is the slug of the image you would like the droplet created with.
region_id:
description:
- This is the slug of the region you would like your server to be created in.
ssh_key_ids:
description:
- Optional, array of SSH key (numeric) ID that you would like to be added to the server.
virtio:
description:
- "Bool, turn on virtio driver in droplet for improved network and storage I/O."
type: bool
default: 'yes'
private_networking:
description:
- "Bool, add an additional, private network interface to droplet for inter-droplet communication."
type: bool
default: 'no'
backups_enabled:
description:
- Optional, Boolean, enables backups for your droplet.
type: bool
default: 'no'
user_data:
description:
- opaque blob of data which is made available to the droplet
ipv6:
description:
- Optional, Boolean, enable IPv6 for your droplet.
type: bool
default: 'no'
wait:
description:
- Wait for the droplet to be in state 'running' before returning. If wait is "no" an ip_address may not be returned.
type: bool
default: 'yes'
wait_timeout:
description:
- How long before wait gives up, in seconds.
default: 300
ssh_pub_key:
description:
- The public SSH key you want to add to your account.
notes:
- Two environment variables can be used, DO_API_KEY and DO_API_TOKEN. They both refer to the v2 token.
- As of Ansible 1.9.5 and 2.0, Version 2 of the DigitalOcean API is used, this removes C(client_id) and C(api_key) options in favor of C(api_token).
- If you are running Ansible 1.9.4 or earlier you might not be able to use the included version of this module as the API version used has been retired.
Upgrade Ansible or, if unable to, try downloading the latest version of this module from github and putting it into a 'library' directory.
requirements:
- "python >= 2.6"
- dopy
'''
EXAMPLES = '''
# Ensure a SSH key is present
# If a key matches this name, will return the ssh key id and changed = False
# If no existing key matches this name, a new key is created, the ssh key id is returned and changed = False
- name: Ensure a SSH key is present
digital_ocean:
state: present
command: ssh
name: my_ssh_key
ssh_pub_key: 'ssh-rsa AAAA...'
api_token: XXX
# Will return the droplet details including the droplet id (used for idempotence)
- name: Create a new Droplet
digital_ocean:
state: present
command: droplet
name: mydroplet
api_token: XXX
size_id: 2gb
region_id: ams2
image_id: fedora-19-x64
wait_timeout: 500
register: my_droplet
- debug:
msg: "ID is {{ my_droplet.droplet.id }}"
- debug:
msg: "IP is {{ my_droplet.droplet.ip_address }}"
# Ensure a droplet is present
# If droplet id already exist, will return the droplet details and changed = False
# If no droplet matches the id, a new droplet will be created and the droplet details (including the new id) are returned, changed = True.
- name: Ensure a droplet is present
digital_ocean:
state: present
command: droplet
id: 123
name: mydroplet
api_token: XXX
size_id: 2gb
region_id: ams2
image_id: fedora-19-x64
wait_timeout: 500
# Create a droplet with ssh key
# The ssh key id can be passed as argument at the creation of a droplet (see ssh_key_ids).
# Several keys can be added to ssh_key_ids as id1,id2,id3
# The keys are used to connect as root to the droplet.
- name: Create a droplet with ssh key
digital_ocean:
state: present
ssh_key_ids: 123,456
name: mydroplet
api_token: XXX
size_id: 2gb
region_id: ams2
image_id: fedora-19-x64
'''
import os
import time
import traceback
from distutils.version import LooseVersion
try:
# Imported as a dependency for dopy
import ansible.module_utils.six
HAS_SIX = True
except ImportError:
HAS_SIX = False
HAS_DOPY = False
try:
import dopy
from dopy.manager import DoError, DoManager
if LooseVersion(dopy.__version__) >= LooseVersion('0.3.2'):
HAS_DOPY = True
except ImportError:
pass
from ansible.module_utils.basic import AnsibleModule, env_fallback
class TimeoutError(Exception):
def __init__(self, msg, id_):
super(TimeoutError, self).__init__(msg)
self.id = id_
class JsonfyMixIn(object):
def to_json(self):
return self.__dict__
class Droplet(JsonfyMixIn):
manager = None
def __init__(self, droplet_json):
self.status = 'new'
self.__dict__.update(droplet_json)
def is_powered_on(self):
return self.status == 'active'
def update_attr(self, attrs=None):
if attrs:
for k, v in attrs.items():
setattr(self, k, v)
networks = attrs.get('networks', {})
for network in networks.get('v6', []):
if network['type'] == 'public':
setattr(self, 'public_ipv6_address', network['ip_address'])
else:
setattr(self, 'private_ipv6_address', network['ip_address'])
else:
json = self.manager.show_droplet(self.id)
if json['ip_address']:
self.update_attr(json)
def power_on(self):
if self.status != 'off':
raise AssertionError('Can only power on a closed one.')
json = self.manager.power_on_droplet(self.id)
self.update_attr(json)
def ensure_powered_on(self, wait=True, wait_timeout=300):
if self.is_powered_on():
return
if self.status == 'off': # powered off
self.power_on()
if wait:
end_time = time.time() + wait_timeout
while time.time() < end_time:
time.sleep(min(20, end_time - time.time()))
self.update_attr()
if self.is_powered_on():
if not self.ip_address:
raise TimeoutError('No ip is found.', self.id)
return
raise TimeoutError('Wait for droplet running timeout', self.id)
def destroy(self):
return self.manager.destroy_droplet(self.id, scrub_data=True)
@classmethod
def setup(cls, api_token):
cls.manager = DoManager(None, api_token, api_version=2)
@classmethod
def add(cls, name, size_id, image_id, region_id, ssh_key_ids=None, virtio=True, private_networking=False, backups_enabled=False, user_data=None,
ipv6=False):
private_networking_lower = str(private_networking).lower()
backups_enabled_lower = str(backups_enabled).lower()
ipv6_lower = str(ipv6).lower()
json = cls.manager.new_droplet(name, size_id, image_id, region_id,
ssh_key_ids=ssh_key_ids, virtio=virtio, private_networking=private_networking_lower,
backups_enabled=backups_enabled_lower, user_data=user_data, ipv6=ipv6_lower)
droplet = cls(json)
return droplet
@classmethod
def find(cls, id=None, name=None):
if not id and not name:
return False
droplets = cls.list_all()
# Check first by id. digital ocean requires that it be unique
for droplet in droplets:
if droplet.id == id:
return droplet
# Failing that, check by hostname.
for droplet in droplets:
if droplet.name == name:
return droplet
return False
@classmethod
def list_all(cls):
json = cls.manager.all_active_droplets()
return list(map(cls, json))
class SSH(JsonfyMixIn):
manager = None
def __init__(self, ssh_key_json):
self.__dict__.update(ssh_key_json)
update_attr = __init__
def destroy(self):
self.manager.destroy_ssh_key(self.id)
return True
@classmethod
def setup(cls, api_token):
cls.manager = DoManager(None, api_token, api_version=2)
@classmethod
def find(cls, name):
if not name:
return False
keys = cls.list_all()
for key in keys:
if key.name == name:
return key
return False
@classmethod
def list_all(cls):
json = cls.manager.all_ssh_keys()
return list(map(cls, json))
@classmethod
def add(cls, name, key_pub):
json = cls.manager.new_ssh_key(name, key_pub)
return cls(json)
def core(module):
def getkeyordie(k):
v = module.params[k]
if v is None:
module.fail_json(msg='Unable to load %s' % k)
return v
api_token = module.params['api_token']
changed = True
command = module.params['command']
state = module.params['state']
if command == 'droplet':
Droplet.setup(api_token)
if state in ('active', 'present'):
# First, try to find a droplet by id.
droplet = Droplet.find(id=module.params['id'])
# If we couldn't find the droplet and the user is allowing unique
# hostnames, then check to see if a droplet with the specified
# hostname already exists.
if not droplet and module.params['unique_name']:
droplet = Droplet.find(name=getkeyordie('name'))
# If both of those attempts failed, then create a new droplet.
if not droplet:
droplet = Droplet.add(
name=getkeyordie('name'),
size_id=getkeyordie('size_id'),
image_id=getkeyordie('image_id'),
region_id=getkeyordie('region_id'),
ssh_key_ids=module.params['ssh_key_ids'],
virtio=module.params['virtio'],
private_networking=module.params['private_networking'],
backups_enabled=module.params['backups_enabled'],
user_data=module.params.get('user_data'),
ipv6=module.params['ipv6'],
)
if droplet.is_powered_on():
changed = False
droplet.ensure_powered_on(
wait=getkeyordie('wait'),
wait_timeout=getkeyordie('wait_timeout')
)
module.exit_json(changed=changed, droplet=droplet.to_json())
elif state in ('absent', 'deleted'):
# First, try to find a droplet by id.
droplet = Droplet.find(module.params['id'])
# If we couldn't find the droplet and the user is allowing unique
# hostnames, then check to see if a droplet with the specified
# hostname already exists.
if not droplet and module.params['unique_name']:
droplet = Droplet.find(name=getkeyordie('name'))
if not droplet:
module.exit_json(changed=False, msg='The droplet is not found.')
droplet.destroy()
module.exit_json(changed=True)
elif command == 'ssh':
SSH.setup(api_token)
name = getkeyordie('name')
if state in ('active', 'present'):
key = SSH.find(name)
if key:
module.exit_json(changed=False, ssh_key=key.to_json())
key = SSH.add(name, getkeyordie('ssh_pub_key'))
module.exit_json(changed=True, ssh_key=key.to_json())
elif state in ('absent', 'deleted'):
key = SSH.find(name)
if not key:
module.exit_json(changed=False, msg='SSH key with the name of %s is not found.' % name)
key.destroy()
module.exit_json(changed=True)
def main():
module = AnsibleModule(
argument_spec=dict(
command=dict(choices=['droplet', 'ssh'], default='droplet'),
state=dict(choices=['active', 'present', 'absent', 'deleted'], default='present'),
api_token=dict(
aliases=['API_TOKEN'],
no_log=True,
fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY'])
),
name=dict(type='str'),
size_id=dict(),
image_id=dict(),
region_id=dict(),
ssh_key_ids=dict(type='list'),
virtio=dict(type='bool', default=True),
private_networking=dict(type='bool', default=False),
backups_enabled=dict(type='bool', default=False),
id=dict(aliases=['droplet_id'], type='int'),
unique_name=dict(type='bool', default=False),
user_data=dict(default=None),
ipv6=dict(type='bool', default=False),
wait=dict(type='bool', default=True),
wait_timeout=dict(default=300, type='int'),
ssh_pub_key=dict(type='str'),
),
required_together=(
['size_id', 'image_id', 'region_id'],
),
mutually_exclusive=(
['size_id', 'ssh_pub_key'],
['image_id', 'ssh_pub_key'],
['region_id', 'ssh_pub_key'],
),
required_one_of=(
['id', 'name'],
),
)
if not HAS_DOPY and not HAS_SIX:
module.fail_json(msg='dopy >= 0.3.2 is required for this module. dopy requires six but six is not installed. '
'Make sure both dopy and six are installed.')
if not HAS_DOPY:
module.fail_json(msg='dopy >= 0.3.2 required for this module')
try:
core(module)
except TimeoutError as e:
module.fail_json(msg=str(e), id=e.id)
except (DoError, Exception) as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_account_info.py

View file

@ -1,81 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_account_info
short_description: Gather information about DigitalOcean User account
description:
- This module can be used to gather information about User account.
- This module was called C(digital_ocean_account_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about user account
digital_ocean_account_info:
oauth_token: "{{ oauth_token }}"
'''
RETURN = '''
data:
description: DigitalOcean account information
returned: success
type: dict
sample: {
"droplet_limit": 10,
"email": "testuser1@gmail.com",
"email_verified": true,
"floating_ip_limit": 3,
"status": "active",
"status_message": "",
"uuid": "aaaaaaaaaaaaaa"
}
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
rest = DigitalOceanHelper(module)
response = rest.get("account")
if response.status_code != 200:
module.fail_json(msg="Failed to fetch 'account' information due to error : %s" % response.json['message'])
module.exit_json(changed=False, data=response.json["account"])
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_account_facts', 'community.general.digital_ocean_account_facts'):
module.deprecate("The 'digital_ocean_account_facts' module has been renamed to 'digital_ocean_account_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1,283 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# 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: digital_ocean_block_storage
short_description: Create/destroy or attach/detach Block Storage volumes in DigitalOcean
description:
- Create/destroy Block Storage volume in DigitalOcean, or attach/detach Block Storage volume to a droplet.
options:
command:
description:
- Which operation do you want to perform.
choices: ['create', 'attach']
required: true
state:
description:
- Indicate desired state of the target.
choices: ['present', 'absent']
required: true
block_size:
description:
- The size of the Block Storage volume in gigabytes. Required when command=create and state=present. If snapshot_id is included, this will be ignored.
volume_name:
description:
- The name of the Block Storage volume.
required: true
description:
description:
- Description of the Block Storage volume.
region:
description:
- The slug of the region where your Block Storage volume should be located in. If snapshot_id is included, this will be ignored.
required: true
snapshot_id:
description:
- The snapshot id you would like the Block Storage volume created with. If included, region and block_size will be ignored and changed to null.
droplet_id:
description:
- The droplet id you want to operate on. Required when command=attach.
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Two environment variables can be used, DO_API_KEY and DO_API_TOKEN.
They both refer to the v2 token.
- If snapshot_id is used, region and block_size will be ignored and changed to null.
author:
- "Harnek Sidhu (@harneksidhu)"
'''
EXAMPLES = '''
- name: Create new Block Storage
digital_ocean_block_storage:
state: present
command: create
api_token: <TOKEN>
region: nyc1
block_size: 10
volume_name: nyc1-block-storage
- name: Delete Block Storage
digital_ocean_block_storage:
state: absent
command: create
api_token: <TOKEN>
region: nyc1
volume_name: nyc1-block-storage
- name: Attach Block Storage to a Droplet
digital_ocean_block_storage:
state: present
command: attach
api_token: <TOKEN>
volume_name: nyc1-block-storage
region: nyc1
droplet_id: <ID>
- name: Detach Block Storage from a Droplet
digital_ocean_block_storage:
state: absent
command: attach
api_token: <TOKEN>
volume_name: nyc1-block-storage
region: nyc1
droplet_id: <ID>
'''
RETURN = '''
id:
description: Unique identifier of a Block Storage volume returned during creation.
returned: changed
type: str
sample: "69b25d9a-494c-12e6-a5af-001f53126b44"
'''
import time
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
class DOBlockStorageException(Exception):
pass
class DOBlockStorage(object):
def __init__(self, module):
self.module = module
self.rest = DigitalOceanHelper(module)
def get_key_or_fail(self, k):
v = self.module.params[k]
if v is None:
self.module.fail_json(msg='Unable to load %s' % k)
return v
def poll_action_for_complete_status(self, action_id):
url = 'actions/{0}'.format(action_id)
end_time = time.time() + self.module.params['timeout']
while time.time() < end_time:
time.sleep(2)
response = self.rest.get(url)
status = response.status_code
json = response.json
if status == 200:
if json['action']['status'] == 'completed':
return True
elif json['action']['status'] == 'errored':
raise DOBlockStorageException(json['message'])
raise DOBlockStorageException('Unable to reach api.digitalocean.com')
def get_attached_droplet_ID(self, volume_name, region):
url = 'volumes?name={0}&region={1}'.format(volume_name, region)
response = self.rest.get(url)
status = response.status_code
json = response.json
if status == 200:
volumes = json['volumes']
if len(volumes) > 0:
droplet_ids = volumes[0]['droplet_ids']
if len(droplet_ids) > 0:
return droplet_ids[0]
return None
else:
raise DOBlockStorageException(json['message'])
def attach_detach_block_storage(self, method, volume_name, region, droplet_id):
data = {
'type': method,
'volume_name': volume_name,
'region': region,
'droplet_id': droplet_id
}
response = self.rest.post('volumes/actions', data=data)
status = response.status_code
json = response.json
if status == 202:
return self.poll_action_for_complete_status(json['action']['id'])
elif status == 200:
return True
elif status == 422:
return False
else:
raise DOBlockStorageException(json['message'])
def create_block_storage(self):
volume_name = self.get_key_or_fail('volume_name')
snapshot_id = self.module.params['snapshot_id']
if snapshot_id:
self.module.params['block_size'] = None
self.module.params['region'] = None
block_size = None
region = None
else:
block_size = self.get_key_or_fail('block_size')
region = self.get_key_or_fail('region')
description = self.module.params['description']
data = {
'size_gigabytes': block_size,
'name': volume_name,
'description': description,
'region': region,
'snapshot_id': snapshot_id,
}
response = self.rest.post("volumes", data=data)
status = response.status_code
json = response.json
if status == 201:
self.module.exit_json(changed=True, id=json['volume']['id'])
elif status == 409 and json['id'] == 'conflict':
self.module.exit_json(changed=False)
else:
raise DOBlockStorageException(json['message'])
def delete_block_storage(self):
volume_name = self.get_key_or_fail('volume_name')
region = self.get_key_or_fail('region')
url = 'volumes?name={0}&region={1}'.format(volume_name, region)
attached_droplet_id = self.get_attached_droplet_ID(volume_name, region)
if attached_droplet_id is not None:
self.attach_detach_block_storage('detach', volume_name, region, attached_droplet_id)
response = self.rest.delete(url)
status = response.status_code
json = response.json
if status == 204:
self.module.exit_json(changed=True)
elif status == 404:
self.module.exit_json(changed=False)
else:
raise DOBlockStorageException(json['message'])
def attach_block_storage(self):
volume_name = self.get_key_or_fail('volume_name')
region = self.get_key_or_fail('region')
droplet_id = self.get_key_or_fail('droplet_id')
attached_droplet_id = self.get_attached_droplet_ID(volume_name, region)
if attached_droplet_id is not None:
if attached_droplet_id == droplet_id:
self.module.exit_json(changed=False)
else:
self.attach_detach_block_storage('detach', volume_name, region, attached_droplet_id)
changed_status = self.attach_detach_block_storage('attach', volume_name, region, droplet_id)
self.module.exit_json(changed=changed_status)
def detach_block_storage(self):
volume_name = self.get_key_or_fail('volume_name')
region = self.get_key_or_fail('region')
droplet_id = self.get_key_or_fail('droplet_id')
changed_status = self.attach_detach_block_storage('detach', volume_name, region, droplet_id)
self.module.exit_json(changed=changed_status)
def handle_request(module):
block_storage = DOBlockStorage(module)
command = module.params['command']
state = module.params['state']
if command == 'create':
if state == 'present':
block_storage.create_block_storage()
elif state == 'absent':
block_storage.delete_block_storage()
elif command == 'attach':
if state == 'present':
block_storage.attach_block_storage()
elif state == 'absent':
block_storage.detach_block_storage()
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
state=dict(choices=['present', 'absent'], required=True),
command=dict(choices=['create', 'attach'], required=True),
block_size=dict(type='int', required=False),
volume_name=dict(type='str', required=True),
description=dict(type='str'),
region=dict(type='str', required=False),
snapshot_id=dict(type='str', required=False),
droplet_id=dict(type='int')
)
module = AnsibleModule(argument_spec=argument_spec)
try:
handle_request(module)
except DOBlockStorageException as e:
module.fail_json(msg=e.message, exception=traceback.format_exc())
except KeyError as e:
module.fail_json(msg='Unable to load %s' % e.message, exception=traceback.format_exc())
if __name__ == '__main__':
main()

View file

@ -1,169 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
#
# 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: digital_ocean_certificate
short_description: Manage certificates in DigitalOcean.
description:
- Create, Retrieve and remove certificates DigitalOcean.
author: "Abhijeet Kasurde (@Akasurde)"
options:
name:
description:
- The name of the certificate.
required: true
private_key:
description:
- A PEM-formatted private key content of SSL Certificate.
leaf_certificate:
description:
- A PEM-formatted public SSL Certificate.
certificate_chain:
description:
- The full PEM-formatted trust chain between the certificate authority's certificate and your domain's SSL certificate.
state:
description:
- Whether the certificate should be present or absent.
default: present
choices: ['present', 'absent']
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Two environment variables can be used, DO_API_KEY, DO_OAUTH_TOKEN and DO_API_TOKEN.
They both refer to the v2 token.
'''
EXAMPLES = '''
- name: Create a certificate
digital_ocean_certificate:
name: production
state: present
private_key: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkM8OI7pRpgyj1I\n-----END PRIVATE KEY-----"
leaf_certificate: "-----BEGIN CERTIFICATE-----\nMIIFDmg2Iaw==\n-----END CERTIFICATE-----"
oauth_token: b7d03a6947b217efb6f3ec3bd365652
- name: Create a certificate using file lookup plugin
digital_ocean_certificate:
name: production
state: present
private_key: "{{ lookup('file', 'test.key') }}"
leaf_certificate: "{{ lookup('file', 'test.cert') }}"
oauth_token: "{{ oauth_token }}"
- name: Create a certificate with trust chain
digital_ocean_certificate:
name: production
state: present
private_key: "{{ lookup('file', 'test.key') }}"
leaf_certificate: "{{ lookup('file', 'test.cert') }}"
certificate_chain: "{{ lookup('file', 'chain.cert') }}"
oauth_token: "{{ oauth_token }}"
- name: Remove a certificate
digital_ocean_certificate:
name: production
state: absent
oauth_token: "{{ oauth_token }}"
'''
RETURN = ''' # '''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
state = module.params['state']
name = module.params['name']
rest = DigitalOceanHelper(module)
results = dict(changed=False)
response = rest.get('certificates')
status_code = response.status_code
resp_json = response.json
if status_code != 200:
module.fail_json(msg="Failed to retrieve certificates for DigitalOcean")
if state == 'present':
for cert in resp_json['certificates']:
if cert['name'] == name:
module.fail_json(msg="Certificate name %s already exists" % name)
# Certificate does not exist, let us create it
cert_data = dict(name=name,
private_key=module.params['private_key'],
leaf_certificate=module.params['leaf_certificate'])
if module.params['certificate_chain'] is not None:
cert_data.update(certificate_chain=module.params['certificate_chain'])
response = rest.post("certificates", data=cert_data)
status_code = response.status_code
if status_code == 500:
module.fail_json(msg="Failed to upload certificates as the certificates are malformed.")
resp_json = response.json
if status_code == 201:
results.update(changed=True, response=resp_json)
elif status_code == 422:
results.update(changed=False, response=resp_json)
elif state == 'absent':
cert_id_del = None
for cert in resp_json['certificates']:
if cert['name'] == name:
cert_id_del = cert['id']
if cert_id_del is not None:
url = "certificates/{0}".format(cert_id_del)
response = rest.delete(url)
if response.status_code == 204:
results.update(changed=True)
else:
results.update(changed=False)
else:
module.fail_json(msg="Failed to find certificate %s" % name)
module.exit_json(**results)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
name=dict(type='str'),
leaf_certificate=dict(type='str'),
private_key=dict(type='str', no_log=True),
state=dict(choices=['present', 'absent'], default='present'),
certificate_chain=dict(type='str')
)
module = AnsibleModule(
argument_spec=argument_spec,
required_if=[('state', 'present', ['name', 'leaf_certificate', 'private_key']),
('state', 'absent', ['name'])
],
)
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e))
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_certificate_info.py

View file

@ -1,113 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_certificate_info
short_description: Gather information about DigitalOcean certificates
description:
- This module can be used to gather information about DigitalOcean provided certificates.
- This module was called C(digital_ocean_certificate_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
certificate_id:
description:
- Certificate ID that can be used to identify and reference a certificate.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all certificates
digital_ocean_certificate_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about certificate with given id
digital_ocean_certificate_info:
oauth_token: "{{ oauth_token }}"
certificate_id: "892071a0-bb95-49bc-8021-3afd67a210bf"
- name: Get not after information about certificate
digital_ocean_certificate_info:
register: resp_out
- set_fact:
not_after_date: "{{ item.not_after }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?name=='web-cert-01']"
- debug: var=not_after_date
'''
RETURN = '''
data:
description: DigitalOcean certificate information
returned: success
type: list
sample: [
{
"id": "892071a0-bb95-49bc-8021-3afd67a210bf",
"name": "web-cert-01",
"not_after": "2017-02-22T00:23:00Z",
"sha1_fingerprint": "dfcc9f57d86bf58e321c2c6c31c7a971be244ac7",
"created_at": "2017-02-08T16:02:37Z"
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
certificate_id = module.params.get('certificate_id', None)
rest = DigitalOceanHelper(module)
base_url = 'certificates?'
if certificate_id is not None:
response = rest.get("%s/%s" % (base_url, certificate_id))
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to retrieve certificates for DigitalOcean")
resp_json = response.json
certificate = resp_json['certificate']
else:
certificate = rest.get_paginated_data(base_url=base_url, data_key_name='certificates')
module.exit_json(changed=False, data=certificate)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
certificate_id=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_certificate_facts', 'community.general.digital_ocean_certificate_facts'):
module.deprecate("The 'digital_ocean_certificate_facts' module has been renamed to 'digital_ocean_certificate_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1,214 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Project
# 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: digital_ocean_domain
short_description: Create/delete a DNS domain in DigitalOcean
description:
- Create/delete a DNS domain in DigitalOcean.
author: "Michael Gregson (@mgregson)"
options:
state:
description:
- Indicate desired state of the target.
default: present
choices: ['present', 'absent']
id:
description:
- Numeric, the droplet id you want to operate on.
aliases: ['droplet_id']
name:
description:
- String, this is the name of the droplet - must be formatted by hostname rules, or the name of a SSH key, or the name of a domain.
ip:
description:
- An 'A' record for '@' ($ORIGIN) will be created with the value 'ip'. 'ip' is an IP version 4 address.
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Environment variables DO_OAUTH_TOKEN can be used for the oauth_token.
- As of Ansible 1.9.5 and 2.0, Version 2 of the DigitalOcean API is used, this removes C(client_id) and C(api_key) options in favor of C(oauth_token).
- If you are running Ansible 1.9.4 or earlier you might not be able to use the included version of this module as the API version used has been retired.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: Create a domain
digital_ocean_domain:
state: present
name: my.digitalocean.domain
ip: 127.0.0.1
# Create a droplet and corresponding domain
- name: Create a droplet
digital_ocean:
state: present
name: test_droplet
size_id: 1gb
region_id: sgp1
image_id: ubuntu-14-04-x64
register: test_droplet
- name: Create a corresponding domain
digital_ocean_domain:
state: present
name: "{{ test_droplet.droplet.name }}.my.domain"
ip: "{{ test_droplet.droplet.ip_address }}"
'''
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
class DoManager(DigitalOceanHelper, object):
def __init__(self, module):
super(DoManager, self).__init__(module)
self.domain_name = module.params.get('name', None)
self.domain_ip = module.params.get('ip', None)
self.domain_id = module.params.get('id', None)
@staticmethod
def jsonify(response):
return response.status_code, response.json
def all_domains(self):
resp = self.get('domains/')
return resp
def find(self):
if self.domain_name is None and self.domain_id is None:
return False
domains = self.all_domains()
status, json = self.jsonify(domains)
for domain in json['domains']:
if domain['name'] == self.domain_name:
return True
return False
def add(self):
params = {'name': self.domain_name, 'ip_address': self.domain_ip}
resp = self.post('domains/', data=params)
status = resp.status_code
json = resp.json
if status == 201:
return json['domain']
else:
return json
def all_domain_records(self):
resp = self.get('domains/%s/records/' % self.domain_name)
return resp.json
def domain_record(self):
resp = self.get('domains/%s' % self.domain_name)
status, json = self.jsonify(resp)
return json
def destroy_domain(self):
resp = self.delete('domains/%s' % self.domain_name)
status, json = self.jsonify(resp)
if status == 204:
return True
else:
return json
def edit_domain_record(self, record):
params = {'name': '@',
'data': self.module.params.get('ip')}
resp = self.put('domains/%s/records/%s' % (self.domain_name, record['id']), data=params)
status, json = self.jsonify(resp)
return json['domain_record']
def create_domain_record(self):
params = {'name': '@',
'type': 'A',
'data': self.module.params.get('ip')}
resp = self.post('domains/%s/records' % (self.domain_name), data=params)
status, json = self.jsonify(resp)
return json['domain_record']
def core(module):
do_manager = DoManager(module)
state = module.params.get('state')
domain = do_manager.find()
if state == 'present':
if not domain:
domain = do_manager.add()
if 'message' in domain:
module.fail_json(changed=False, msg=domain['message'])
else:
module.exit_json(changed=True, domain=domain)
else:
records = do_manager.all_domain_records()
at_record = None
for record in records['domain_records']:
if record['name'] == "@" and record['type'] == 'A':
at_record = record
if not at_record:
do_manager.create_domain_record()
module.exit_json(changed=True, domain=do_manager.find())
elif not at_record['data'] == module.params.get('ip'):
do_manager.edit_domain_record(at_record)
module.exit_json(changed=True, domain=do_manager.find())
else:
module.exit_json(changed=False, domain=do_manager.domain_record())
elif state == 'absent':
if not domain:
module.exit_json(changed=False, msg="Domain not found")
else:
delete_event = do_manager.destroy_domain()
if not delete_event:
module.fail_json(changed=False, msg=delete_event['message'])
else:
module.exit_json(changed=True, event=None)
delete_event = do_manager.destroy_domain()
module.exit_json(changed=delete_event)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
state=dict(choices=['present', 'absent'], default='present'),
name=dict(type='str'),
id=dict(aliases=['droplet_id'], type='int'),
ip=dict(type='str')
)
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=(
['id', 'name'],
),
)
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_domain_info.py

View file

@ -1,138 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_domain_info
short_description: Gather information about DigitalOcean Domains
description:
- This module can be used to gather information about DigitalOcean provided Domains.
- This module was called C(digital_ocean_domain_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
domain_name:
description:
- Name of the domain to gather information for.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all domains
digital_ocean_domain_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about domain with given name
digital_ocean_domain_info:
oauth_token: "{{ oauth_token }}"
domain_name: "example.com"
- name: Get ttl from domain
digital_ocean_domain_info:
register: resp_out
- set_fact:
domain_ttl: "{{ item.ttl }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?name=='example.com']"
- debug: var=domain_ttl
'''
RETURN = '''
data:
description: DigitalOcean Domain information
returned: success
type: list
sample: [
{
"domain_records": [
{
"data": "ns1.digitalocean.com",
"flags": null,
"id": 37826823,
"name": "@",
"port": null,
"priority": null,
"tag": null,
"ttl": 1800,
"type": "NS",
"weight": null
},
],
"name": "myexample123.com",
"ttl": 1800,
"zone_file": "myexample123.com. IN SOA ns1.digitalocean.com. hostmaster.myexample123.com. 1520702984 10800 3600 604800 1800\n",
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
domain_name = module.params.get('domain_name', None)
rest = DigitalOceanHelper(module)
domain_results = []
if domain_name is not None:
response = rest.get("domains/%s" % domain_name)
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to retrieve domain for DigitalOcean")
resp_json = response.json
domains = [resp_json['domain']]
else:
domains = rest.get_paginated_data(base_url="domains?", data_key_name='domains')
for temp_domain in domains:
temp_domain_dict = {
"name": temp_domain['name'],
"ttl": temp_domain['ttl'],
"zone_file": temp_domain['zone_file'],
"domain_records": list(),
}
base_url = "domains/%s/records?" % temp_domain['name']
temp_domain_dict["domain_records"] = rest.get_paginated_data(base_url=base_url, data_key_name='domain_records')
domain_results.append(temp_domain_dict)
module.exit_json(changed=False, data=domain_results)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
domain_name=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_domain_facts', 'community.general.digital_ocean_domain_facts'):
module.deprecate("The 'digital_ocean_domain_facts' module has been renamed to 'digital_ocean_domain_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1,351 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Project
# 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: digital_ocean_droplet
short_description: Create and delete a DigitalOcean droplet
description:
- Create and delete a droplet in DigitalOcean and optionally wait for it to be active.
author: "Gurchet Rai (@gurch101)"
options:
state:
description:
- Indicate desired state of the target.
default: present
choices: ['present', 'absent']
id:
description:
- Numeric, the droplet id you want to operate on.
aliases: ['droplet_id']
name:
description:
- String, this is the name of the droplet - must be formatted by hostname rules.
unique_name:
description:
- require unique hostnames. By default, DigitalOcean allows multiple hosts with the same name. Setting this to "yes" allows only one host
per name. Useful for idempotence.
default: False
type: bool
size:
description:
- This is the slug of the size you would like the droplet created with.
aliases: ['size_id']
image:
description:
- This is the slug of the image you would like the droplet created with.
aliases: ['image_id']
region:
description:
- This is the slug of the region you would like your server to be created in.
aliases: ['region_id']
ssh_keys:
description:
- array of SSH key Fingerprint that you would like to be added to the server.
required: False
private_networking:
description:
- add an additional, private network interface to droplet for inter-droplet communication.
default: False
type: bool
vpc_uuid:
description:
- A string specifying the UUID of the VPC to which the Droplet will be assigned. If excluded, Droplet will be
assigned to the account's default VPC for the region.
type: str
version_added: 0.2.0
user_data:
description:
- opaque blob of data which is made available to the droplet
required: False
ipv6:
description:
- enable IPv6 for your droplet.
required: False
default: False
type: bool
wait:
description:
- Wait for the droplet to be active before returning. If wait is "no" an ip_address may not be returned.
required: False
default: True
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds, when creating a droplet.
default: 120
backups:
description:
- indicates whether automated backups should be enabled.
required: False
default: False
type: bool
monitoring:
description:
- indicates whether to install the DigitalOcean agent for monitoring.
required: False
default: False
type: bool
tags:
description:
- List, A list of tag names as strings to apply to the Droplet after it is created. Tag names can either be existing or new tags.
required: False
volumes:
description:
- List, A list including the unique string identifier for each Block Storage volume to be attached to the Droplet.
required: False
oauth_token:
description:
- DigitalOcean OAuth token. Can be specified in C(DO_API_KEY), C(DO_API_TOKEN), or C(DO_OAUTH_TOKEN) environment variables
aliases: ['API_TOKEN']
required: True
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: Create a new droplet
digital_ocean_droplet:
state: present
name: mydroplet
oauth_token: XXX
size: 2gb
region: sfo1
image: ubuntu-16-04-x64
wait_timeout: 500
ssh_keys: [ .... ]
register: my_droplet
- debug:
msg: "ID is {{ my_droplet.data.droplet.id }}, IP is {{ my_droplet.data.ip_address }}"
- name: Ensure a droplet is present
digital_ocean_droplet:
state: present
id: 123
name: mydroplet
oauth_token: XXX
size: 2gb
region: sfo1
image: ubuntu-16-04-x64
wait_timeout: 500
- name: Ensure a droplet is present with SSH keys installed
digital_ocean_droplet:
state: present
id: 123
name: mydroplet
oauth_token: XXX
size: 2gb
region: sfo1
ssh_keys: ['1534404', '1784768']
image: ubuntu-16-04-x64
wait_timeout: 500
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#droplets
data:
description: a DigitalOcean Droplet
returned: changed
type: dict
sample: {
"ip_address": "104.248.118.172",
"ipv6_address": "2604:a880:400:d1::90a:6001",
"private_ipv4_address": "10.136.122.141",
"droplet": {
"id": 3164494,
"name": "example.com",
"memory": 512,
"vcpus": 1,
"disk": 20,
"locked": true,
"status": "new",
"kernel": {
"id": 2233,
"name": "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic",
"version": "3.13.0-37-generic"
},
"created_at": "2014-11-14T16:36:31Z",
"features": ["virtio"],
"backup_ids": [],
"snapshot_ids": [],
"image": {},
"volume_ids": [],
"size": {},
"size_slug": "512mb",
"networks": {},
"region": {},
"tags": ["web"]
}
}
'''
import time
import json
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
class DODroplet(object):
def __init__(self, module):
self.rest = DigitalOceanHelper(module)
self.module = module
self.wait = self.module.params.pop('wait', True)
self.wait_timeout = self.module.params.pop('wait_timeout', 120)
self.unique_name = self.module.params.pop('unique_name', False)
# pop the oauth token so we don't include it in the POST data
self.module.params.pop('oauth_token')
def get_by_id(self, droplet_id):
if not droplet_id:
return None
response = self.rest.get('droplets/{0}'.format(droplet_id))
json_data = response.json
if response.status_code == 200:
return json_data
return None
def get_by_name(self, droplet_name):
if not droplet_name:
return None
page = 1
while page is not None:
response = self.rest.get('droplets?page={0}'.format(page))
json_data = response.json
if response.status_code == 200:
for droplet in json_data['droplets']:
if droplet['name'] == droplet_name:
return {'droplet': droplet}
if 'links' in json_data and 'pages' in json_data['links'] and 'next' in json_data['links']['pages']:
page += 1
else:
page = None
return None
def get_addresses(self, data):
"""
Expose IP addresses as their own property allowing users extend to additional tasks
"""
_data = data
for k, v in data.items():
setattr(self, k, v)
networks = _data['droplet']['networks']
for network in networks.get('v4', []):
if network['type'] == 'public':
_data['ip_address'] = network['ip_address']
else:
_data['private_ipv4_address'] = network['ip_address']
for network in networks.get('v6', []):
if network['type'] == 'public':
_data['ipv6_address'] = network['ip_address']
else:
_data['private_ipv6_address'] = network['ip_address']
return _data
def get_droplet(self):
json_data = self.get_by_id(self.module.params['id'])
if not json_data and self.unique_name:
json_data = self.get_by_name(self.module.params['name'])
return json_data
def create(self):
json_data = self.get_droplet()
droplet_data = None
if json_data:
droplet_data = self.get_addresses(json_data)
self.module.exit_json(changed=False, data=droplet_data)
if self.module.check_mode:
self.module.exit_json(changed=True)
request_params = dict(self.module.params)
del request_params['id']
response = self.rest.post('droplets', data=request_params)
json_data = response.json
if response.status_code >= 400:
self.module.fail_json(changed=False, msg=json_data['message'])
if self.wait:
json_data = self.ensure_power_on(json_data['droplet']['id'])
droplet_data = self.get_addresses(json_data)
self.module.exit_json(changed=True, data=droplet_data)
def delete(self):
json_data = self.get_droplet()
if json_data:
if self.module.check_mode:
self.module.exit_json(changed=True)
response = self.rest.delete('droplets/{0}'.format(json_data['droplet']['id']))
json_data = response.json
if response.status_code == 204:
self.module.exit_json(changed=True, msg='Droplet deleted')
self.module.fail_json(changed=False, msg='Failed to delete droplet')
else:
self.module.exit_json(changed=False, msg='Droplet not found')
def ensure_power_on(self, droplet_id):
end_time = time.time() + self.wait_timeout
while time.time() < end_time:
response = self.rest.get('droplets/{0}'.format(droplet_id))
json_data = response.json
if json_data['droplet']['status'] == 'active':
return json_data
time.sleep(min(2, end_time - time.time()))
self.module.fail_json(msg='Wait for droplet powering on timeout')
def core(module):
state = module.params.pop('state')
droplet = DODroplet(module)
if state == 'present':
droplet.create()
elif state == 'absent':
droplet.delete()
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(choices=['present', 'absent'], default='present'),
oauth_token=dict(
aliases=['API_TOKEN'],
no_log=True,
fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN'])
),
name=dict(type='str'),
size=dict(aliases=['size_id']),
image=dict(aliases=['image_id']),
region=dict(aliases=['region_id']),
ssh_keys=dict(type='list'),
private_networking=dict(type='bool', default=False),
vpc_uuid=dict(type='str'),
backups=dict(type='bool', default=False),
monitoring=dict(type='bool', default=False),
id=dict(aliases=['droplet_id'], type='int'),
user_data=dict(default=None),
ipv6=dict(type='bool', default=False),
volumes=dict(type='list'),
tags=dict(type='list'),
wait=dict(type='bool', default=True),
wait_timeout=dict(default=120, type='int'),
unique_name=dict(type='bool', default=False),
),
required_one_of=(
['id', 'name'],
),
required_if=([
('state', 'present', ['name', 'size', 'image', 'region']),
]),
supports_check_mode=True,
)
core(module)
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_firewall_info.py

View file

@ -1,131 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Anthony Bond <ajbond2005@gmail.com>
# 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: digital_ocean_firewall_info
short_description: Gather information about DigitalOcean firewalls
description:
- This module can be used to gather information about DigitalOcean firewalls.
- This module was called C(digital_ocean_firewall_facts) before Ansible 2.9. The usage did not change.
author: "Anthony Bond (@BondAnthony)"
options:
name:
description:
- Firewall rule name that can be used to identify and reference a specific firewall rule.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all firewalls
digital_ocean_firewall_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about a specific firewall by name
digital_ocean_firewall_info:
oauth_token: "{{ oauth_token }}"
name: "firewall_name"
- name: Gather information from a firewall rule
digital_ocean_firewall_info:
name: SSH
register: resp_out
- set_fact:
firewall_id: "{{ resp_out.data.id }}"
- debug:
msg: "{{ firewall_id }}"
'''
RETURN = '''
data:
description: DigitalOcean firewall information
returned: success
type: list
sample: [
{
"id": "435tbg678-1db53-32b6-t543-28322569t252",
"name": "metrics",
"status": "succeeded",
"inbound_rules": [
{
"protocol": "tcp",
"ports": "9100",
"sources": {
"addresses": [
"1.1.1.1"
]
}
}
],
"outbound_rules": [],
"created_at": "2018-01-15T07:04:25Z",
"droplet_ids": [
87426985
],
"tags": [],
"pending_changes": []
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
firewall_name = module.params.get('name', None)
rest = DigitalOceanHelper(module)
base_url = 'firewalls?'
response = rest.get("%s" % base_url)
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to retrieve firewalls from Digital Ocean")
firewalls = rest.get_paginated_data(base_url=base_url, data_key_name='firewalls')
if firewall_name is not None:
rule = {}
for firewall in firewalls:
if firewall['name'] == firewall_name:
rule.update(firewall)
module.exit_json(changed=False, data=rule)
else:
module.exit_json(changed=False, data=firewalls)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
name=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_firewall_facts', 'community.general.digital_ocean_firewall_facts'):
module.deprecate("The 'digital_ocean_firewall_facts' module has been renamed to 'digital_ocean_firewall_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1,311 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Patrick F. Marques <patrickfmarques@gmail.com>
# 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: digital_ocean_floating_ip
short_description: Manage DigitalOcean Floating IPs
description:
- Create/delete/assign a floating IP.
author: "Patrick Marques (@pmarques)"
options:
state:
description:
- Indicate desired state of the target.
default: present
choices: ['present', 'absent']
ip:
description:
- Public IP address of the Floating IP. Used to remove an IP
region:
description:
- The region that the Floating IP is reserved to.
droplet_id:
description:
- The Droplet that the Floating IP has been assigned to.
oauth_token:
description:
- DigitalOcean OAuth token.
required: true
notes:
- Version 2 of DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: "Create a Floating IP in region lon1"
digital_ocean_floating_ip:
state: present
region: lon1
- name: "Create a Floating IP assigned to Droplet ID 123456"
digital_ocean_floating_ip:
state: present
droplet_id: 123456
- name: "Delete a Floating IP with ip 1.2.3.4"
digital_ocean_floating_ip:
state: absent
ip: "1.2.3.4"
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#floating-ips
data:
description: a DigitalOcean Floating IP resource
returned: success and no resource constraint
type: dict
sample: {
"action": {
"id": 68212728,
"status": "in-progress",
"type": "assign_ip",
"started_at": "2015-10-15T17:45:44Z",
"completed_at": null,
"resource_id": 758603823,
"resource_type": "floating_ip",
"region": {
"name": "New York 3",
"slug": "nyc3",
"sizes": [
"512mb",
"1gb",
"2gb",
"4gb",
"8gb",
"16gb",
"32gb",
"48gb",
"64gb"
],
"features": [
"private_networking",
"backups",
"ipv6",
"metadata"
],
"available": true
},
"region_slug": "nyc3"
}
}
'''
import json
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.urls import fetch_url
class Response(object):
def __init__(self, resp, info):
self.body = None
if resp:
self.body = resp.read()
self.info = info
@property
def json(self):
if not self.body:
if "body" in self.info:
return json.loads(self.info["body"])
return None
try:
return json.loads(self.body)
except ValueError:
return None
@property
def status_code(self):
return self.info["status"]
class Rest(object):
def __init__(self, module, headers):
self.module = module
self.headers = headers
self.baseurl = 'https://api.digitalocean.com/v2'
def _url_builder(self, path):
if path[0] == '/':
path = path[1:]
return '%s/%s' % (self.baseurl, path)
def send(self, method, path, data=None, headers=None):
url = self._url_builder(path)
data = self.module.jsonify(data)
timeout = self.module.params['timeout']
resp, info = fetch_url(self.module, url, data=data, headers=self.headers, method=method, timeout=timeout)
# Exceptions in fetch_url may result in a status -1, the ensures a
if info['status'] == -1:
self.module.fail_json(msg=info['msg'])
return Response(resp, info)
def get(self, path, data=None, headers=None):
return self.send('GET', path, data, headers)
def put(self, path, data=None, headers=None):
return self.send('PUT', path, data, headers)
def post(self, path, data=None, headers=None):
return self.send('POST', path, data, headers)
def delete(self, path, data=None, headers=None):
return self.send('DELETE', path, data, headers)
def wait_action(module, rest, ip, action_id, timeout=10):
end_time = time.time() + 10
while time.time() < end_time:
response = rest.get('floating_ips/{0}/actions/{1}'.format(ip, action_id))
status_code = response.status_code
status = response.json['action']['status']
# TODO: check status_code == 200?
if status == 'completed':
return True
elif status == 'errored':
module.fail_json(msg='Floating ip action error [ip: {0}: action: {1}]'.format(
ip, action_id), data=json)
module.fail_json(msg='Floating ip action timeout [ip: {0}: action: {1}]'.format(
ip, action_id), data=json)
def core(module):
api_token = module.params['oauth_token']
state = module.params['state']
ip = module.params['ip']
droplet_id = module.params['droplet_id']
rest = Rest(module, {'Authorization': 'Bearer {0}'.format(api_token),
'Content-type': 'application/json'})
if state in ('present'):
if droplet_id is not None and module.params['ip'] is not None:
# Lets try to associate the ip to the specified droplet
associate_floating_ips(module, rest)
else:
create_floating_ips(module, rest)
elif state in ('absent'):
response = rest.delete("floating_ips/{0}".format(ip))
status_code = response.status_code
json_data = response.json
if status_code == 204:
module.exit_json(changed=True)
elif status_code == 404:
module.exit_json(changed=False)
else:
module.exit_json(changed=False, data=json_data)
def get_floating_ip_details(module, rest):
ip = module.params['ip']
response = rest.get("floating_ips/{0}".format(ip))
status_code = response.status_code
json_data = response.json
if status_code == 200:
return json_data['floating_ip']
else:
module.fail_json(msg="Error assigning floating ip [{0}: {1}]".format(
status_code, json_data["message"]), region=module.params['region'])
def assign_floating_id_to_droplet(module, rest):
ip = module.params['ip']
payload = {
"type": "assign",
"droplet_id": module.params['droplet_id'],
}
response = rest.post("floating_ips/{0}/actions".format(ip), data=payload)
status_code = response.status_code
json_data = response.json
if status_code == 201:
wait_action(module, rest, ip, json_data['action']['id'])
module.exit_json(changed=True, data=json_data)
else:
module.fail_json(msg="Error creating floating ip [{0}: {1}]".format(
status_code, json_data["message"]), region=module.params['region'])
def associate_floating_ips(module, rest):
floating_ip = get_floating_ip_details(module, rest)
droplet = floating_ip['droplet']
# TODO: If already assigned to a droplet verify if is one of the specified as valid
if droplet is not None and str(droplet['id']) in [module.params['droplet_id']]:
module.exit_json(changed=False)
else:
assign_floating_id_to_droplet(module, rest)
def create_floating_ips(module, rest):
payload = {
}
if module.params['region'] is not None:
payload["region"] = module.params['region']
if module.params['droplet_id'] is not None:
payload["droplet_id"] = module.params['droplet_id']
response = rest.post("floating_ips", data=payload)
status_code = response.status_code
json_data = response.json
if status_code == 202:
module.exit_json(changed=True, data=json_data)
else:
module.fail_json(msg="Error creating floating ip [{0}: {1}]".format(
status_code, json_data["message"]), region=module.params['region'])
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(choices=['present', 'absent'], default='present'),
ip=dict(aliases=['id'], required=False),
region=dict(required=False),
droplet_id=dict(required=False),
oauth_token=dict(
no_log=True,
# Support environment variable for DigitalOcean OAuth Token
fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN']),
required=True,
),
validate_certs=dict(type='bool', default=True),
timeout=dict(type='int', default=30),
),
required_if=[
('state', 'delete', ['ip'])
],
mutually_exclusive=[
['region', 'droplet_id']
],
)
core(module)
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_floating_ip_info.py

View file

@ -1,119 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (C) 2017-18, Ansible Project
# 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: digital_ocean_floating_ip_info
short_description: DigitalOcean Floating IPs information
description:
- This module can be used to fetch DigitalOcean Floating IPs information.
- This module was called C(digital_ocean_floating_ip_facts) before Ansible 2.9. The usage did not change.
author: "Patrick Marques (@pmarques)"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Version 2 of DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: "Gather information about all Floating IPs"
digital_ocean_floating_ip_info:
register: result
- name: "List of current floating ips"
debug: var=result.floating_ips
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#floating-ips
floating_ips:
description: a DigitalOcean Floating IP resource
returned: success and no resource constraint
type: list
sample: [
{
"ip": "45.55.96.47",
"droplet": null,
"region": {
"name": "New York 3",
"slug": "nyc3",
"sizes": [
"512mb",
"1gb",
"2gb",
"4gb",
"8gb",
"16gb",
"32gb",
"48gb",
"64gb"
],
"features": [
"private_networking",
"backups",
"ipv6",
"metadata"
],
"available": true
},
"locked": false
}
]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
rest = DigitalOceanHelper(module)
page = 1
has_next = True
floating_ips = []
status_code = None
while has_next or status_code != 200:
response = rest.get("floating_ips?page={0}&per_page=20".format(page))
status_code = response.status_code
# stop if any error during pagination
if status_code != 200:
break
page += 1
floating_ips.extend(response.json["floating_ips"])
has_next = "pages" in response.json["links"] and "next" in response.json["links"]["pages"]
if status_code == 200:
module.exit_json(changed=False, floating_ips=floating_ips)
else:
module.fail_json(msg="Error fetching information [{0}: {1}]".format(
status_code, response.json["message"]))
def main():
module = AnsibleModule(
argument_spec=DigitalOceanHelper.digital_ocean_argument_spec()
)
if module._name in ('digital_ocean_floating_ip_facts', 'community.general.digital_ocean_floating_ip_facts'):
module.deprecate("The 'digital_ocean_floating_ip_facts' module has been renamed to 'digital_ocean_floating_ip_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e))
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_image_info.py

View file

@ -1,148 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_image_info
short_description: Gather information about DigitalOcean images
description:
- This module can be used to gather information about DigitalOcean provided images.
- These images can be either of type C(distribution), C(application) and C(private).
- This module was called C(digital_ocean_image_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
image_type:
description:
- Specifies the type of image information to be retrieved.
- If set to C(application), then information are gathered related to all application images.
- If set to C(distribution), then information are gathered related to all distribution images.
- If set to C(private), then information are gathered related to all private images.
- If not set to any of above, then information are gathered related to all images.
default: 'all'
choices: [ 'all', 'application', 'distribution', 'private' ]
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all images
digital_ocean_image_info:
image_type: all
oauth_token: "{{ oauth_token }}"
- name: Gather information about application images
digital_ocean_image_info:
image_type: application
oauth_token: "{{ oauth_token }}"
- name: Gather information about distribution images
digital_ocean_image_info:
image_type: distribution
oauth_token: "{{ oauth_token }}"
- name: Get distribution about image with slug coreos-beta
digital_ocean_image_info:
register: resp_out
- set_fact:
distribution_name: "{{ item.distribution }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?slug=='coreos-beta']"
- debug: var=distribution_name
'''
RETURN = '''
data:
description: DigitalOcean image information
returned: success
type: list
sample: [
{
"created_at": "2018-02-02T07:11:43Z",
"distribution": "CoreOS",
"id": 31434061,
"min_disk_size": 20,
"name": "1662.1.0 (beta)",
"public": true,
"regions": [
"nyc1",
"sfo1",
"nyc2",
"ams2",
"sgp1",
"lon1",
"nyc3",
"ams3",
"fra1",
"tor1",
"sfo2",
"blr1"
],
"size_gigabytes": 0.42,
"slug": "coreos-beta",
"type": "snapshot"
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
image_type = module.params['image_type']
rest = DigitalOceanHelper(module)
base_url = 'images?'
if image_type == 'distribution':
base_url += "type=distribution&"
elif image_type == 'application':
base_url += "type=application&"
elif image_type == 'private':
base_url += "private=true&"
images = rest.get_paginated_data(base_url=base_url, data_key_name='images')
module.exit_json(changed=False, data=images)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
image_type=dict(type='str',
required=False,
choices=['all', 'application', 'distribution', 'private'],
default='all'
)
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_image_facts', 'community.general.digital_ocean_image_facts'):
module.deprecate("The 'digital_ocean_image_facts' module has been renamed to 'digital_ocean_image_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_load_balancer_info.py

View file

@ -1,115 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_load_balancer_info
short_description: Gather information about DigitalOcean load balancers
description:
- This module can be used to gather information about DigitalOcean provided load balancers.
- This module was called C(digital_ocean_load_balancer_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
load_balancer_id:
description:
- Load balancer ID that can be used to identify and reference a load_balancer.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all load balancers
digital_ocean_load_balancer_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about load balancer with given id
digital_ocean_load_balancer_info:
oauth_token: "{{ oauth_token }}"
load_balancer_id: "4de7ac8b-495b-4884-9a69-1050c6793cd6"
- name: Get name from load balancer id
digital_ocean_load_balancer_info:
register: resp_out
- set_fact:
load_balancer_name: "{{ item.name }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?id=='4de7ac8b-495b-4884-9a69-1050c6793cd6']"
- debug: var=load_balancer_name
'''
RETURN = '''
data:
description: DigitalOcean Load balancer information
returned: success
type: list
sample: [
{
"id": "4de7ac8b-495b-4884-9a69-1050c6793cd6",
"name": "example-lb-01",
"ip": "104.131.186.241",
"algorithm": "round_robin",
"status": "new",
"created_at": "2017-02-01T22:22:58Z",
...
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
load_balancer_id = module.params.get('load_balancer_id', None)
rest = DigitalOceanHelper(module)
base_url = 'load_balancers?'
if load_balancer_id is not None:
response = rest.get("%s/%s" % (base_url, load_balancer_id))
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to retrieve load balancers for DigitalOcean")
resp_json = response.json
load_balancer = resp_json['load_balancer']
else:
load_balancer = rest.get_paginated_data(base_url=base_url, data_key_name='load_balancers')
module.exit_json(changed=False, data=load_balancer)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
load_balancer_id=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_load_balancer_facts', 'community.general.digital_ocean_load_balancer_facts'):
module.deprecate("The 'digital_ocean_load_balancer_facts' module has been renamed to 'digital_ocean_load_balancer_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_region_info.py

View file

@ -1,115 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_region_info
short_description: Gather information about DigitalOcean regions
description:
- This module can be used to gather information about regions.
- This module was called C(digital_ocean_region_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: Gather information about all regions
digital_ocean_region_info:
oauth_token: "{{ oauth_token }}"
- name: Get Name of region where slug is known
digital_ocean_region_info:
oauth_token: "{{ oauth_token }}"
register: resp_out
- debug: var=resp_out
- set_fact:
region_slug: "{{ item.name }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?slug==`nyc1`]"
- debug: var=region_slug
'''
RETURN = '''
data:
description: DigitalOcean regions information
returned: success
type: list
sample: [
{
"available": true,
"features": [
"private_networking",
"backups",
"ipv6",
"metadata",
"install_agent",
"storage"
],
"name": "New York 1",
"sizes": [
"512mb",
"s-1vcpu-1gb",
"1gb",
"s-3vcpu-1gb",
"s-1vcpu-2gb",
"s-2vcpu-2gb",
"2gb",
"s-1vcpu-3gb",
"s-2vcpu-4gb",
"4gb",
"c-2",
"m-1vcpu-8gb",
"8gb",
"s-4vcpu-8gb",
"s-6vcpu-16gb",
"16gb"
],
"slug": "nyc1"
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
rest = DigitalOceanHelper(module)
base_url = 'regions?'
regions = rest.get_paginated_data(base_url=base_url, data_key_name='regions')
module.exit_json(changed=False, data=regions)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_region_facts', 'community.general.digital_ocean_region_facts'):
module.deprecate("The 'digital_ocean_region_facts' module has been renamed to 'digital_ocean_region_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_size_info.py

View file

@ -1,113 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_size_info
short_description: Gather information about DigitalOcean Droplet sizes
description:
- This module can be used to gather information about droplet sizes.
- This module was called C(digital_ocean_size_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all droplet sizes
digital_ocean_size_info:
oauth_token: "{{ oauth_token }}"
- name: Get droplet Size Slug where vcpus is 1
digital_ocean_size_info:
oauth_token: "{{ oauth_token }}"
register: resp_out
- debug: var=resp_out
- set_fact:
size_slug: "{{ item.slug }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?vcpus==`1`]"
- debug: var=size_slug
'''
RETURN = '''
data:
description: DigitalOcean droplet size information
returned: success
type: list
sample: [
{
"available": true,
"disk": 20,
"memory": 512,
"price_hourly": 0.00744,
"price_monthly": 5.0,
"regions": [
"ams2",
"ams3",
"blr1",
"fra1",
"lon1",
"nyc1",
"nyc2",
"nyc3",
"sfo1",
"sfo2",
"sgp1",
"tor1"
],
"slug": "512mb",
"transfer": 1.0,
"vcpus": 1
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
rest = DigitalOceanHelper(module)
response = rest.get('sizes')
if response.status_code != 200:
module.fail_json(msg="Failed to fetch 'sizes' information due to error : %s" % response.json['message'])
module.exit_json(changed=False, data=response.json['sizes'])
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
module = AnsibleModule(
argument_spec=argument_spec,
)
if module._name in ('digital_ocean_size_facts', 'community.general.digital_ocean_size_facts'):
module.deprecate("The 'digital_ocean_size_facts' module has been renamed to 'digital_ocean_size_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_snapshot_info.py

View file

@ -1,160 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_snapshot_info
short_description: Gather information about DigitalOcean Snapshot
description:
- This module can be used to gather information about snapshot information based upon provided values such as droplet, volume and snapshot id.
- This module was called C(digital_ocean_snapshot_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
snapshot_type:
description:
- Specifies the type of snapshot information to be retrieved.
- If set to C(droplet), then information are gathered related to snapshots based on Droplets only.
- If set to C(volume), then information are gathered related to snapshots based on volumes only.
- If set to C(by_id), then information are gathered related to snapshots based on snapshot id only.
- If not set to any of the above, then information are gathered related to all snapshots.
default: 'all'
choices: [ 'all', 'droplet', 'volume', 'by_id']
required: false
snapshot_id:
description:
- To retrieve information about a snapshot, please specify this as a snapshot id.
- If set to actual snapshot id, then information are gathered related to that particular snapshot only.
- This is required parameter, if C(snapshot_type) is set to C(by_id).
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all snapshots
digital_ocean_snapshot_info:
snapshot_type: all
oauth_token: "{{ oauth_token }}"
- name: Gather information about droplet snapshots
digital_ocean_snapshot_info:
snapshot_type: droplet
oauth_token: "{{ oauth_token }}"
- name: Gather information about volume snapshots
digital_ocean_snapshot_info:
snapshot_type: volume
oauth_token: "{{ oauth_token }}"
- name: Gather information about snapshot by snapshot id
digital_ocean_snapshot_info:
snapshot_type: by_id
snapshot_id: 123123123
oauth_token: "{{ oauth_token }}"
- name: Get information about snapshot named big-data-snapshot1
digital_ocean_snapshot_info:
register: resp_out
- set_fact:
snapshot_id: "{{ item.id }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?name=='big-data-snapshot1']"
- debug: var=snapshot_id
'''
RETURN = '''
data:
description: DigitalOcean snapshot information
returned: success
type: list
sample: [
{
"id": "4f60fc64-85d1-11e6-a004-000f53315871",
"name": "big-data-snapshot1",
"regions": [
"nyc1"
],
"created_at": "2016-09-28T23:14:30Z",
"resource_id": "89bcc42f-85cf-11e6-a004-000f53315871",
"resource_type": "volume",
"min_disk_size": 10,
"size_gigabytes": 0
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
snapshot_type = module.params['snapshot_type']
rest = DigitalOceanHelper(module)
base_url = 'snapshots?'
snapshot = []
if snapshot_type == 'by_id':
base_url += "/{0}".format(module.params.get('snapshot_id'))
response = rest.get(base_url)
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to fetch snapshot information due to error : %s" % response.json['message'])
snapshot.extend(response.json["snapshot"])
else:
if snapshot_type == 'droplet':
base_url += "resource_type=droplet&"
elif snapshot_type == 'volume':
base_url += "resource_type=volume&"
snapshot = rest.get_paginated_data(base_url=base_url, data_key_name='snapshots')
module.exit_json(changed=False, data=snapshot)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
snapshot_type=dict(type='str',
required=False,
choices=['all', 'droplet', 'volume', 'by_id'],
default='all'),
snapshot_id=dict(type='str',
required=False),
)
module = AnsibleModule(
argument_spec=argument_spec,
required_if=[
['snapshot_type', 'by_id', ['snapshot_id']],
],
)
if module._name in ('digital_ocean_snapshot_facts', 'community.general.digital_ocean_snapshot_facts'):
module.deprecate("The 'digital_ocean_snapshot_facts' module has been renamed to 'digital_ocean_snapshot_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1,257 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# 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: digital_ocean_sshkey
short_description: Manage DigitalOcean SSH keys
description:
- Create/delete DigitalOcean SSH keys.
author: "Patrick Marques (@pmarques)"
options:
state:
description:
- Indicate desired state of the target.
default: present
choices: ['present', 'absent']
fingerprint:
description:
- This is a unique identifier for the SSH key used to delete a key
aliases: ['id']
name:
description:
- The name for the SSH key
ssh_pub_key:
description:
- The Public SSH key to add.
oauth_token:
description:
- DigitalOcean OAuth token.
required: true
notes:
- Version 2 of DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: "Create ssh key"
digital_ocean_sshkey:
oauth_token: "{{ oauth_token }}"
name: "My SSH Public Key"
ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example"
state: present
register: result
- name: "Delete ssh key"
digital_ocean_sshkey:
oauth_token: "{{ oauth_token }}"
state: "absent"
fingerprint: "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa"
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#list-all-keys
data:
description: This is only present when C(state=present)
returned: when C(state=present)
type: dict
sample: {
"ssh_key": {
"id": 512189,
"fingerprint": "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa",
"name": "My SSH Public Key",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example"
}
}
'''
import json
import hashlib
import base64
from ansible.module_utils.basic import env_fallback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
class Response(object):
def __init__(self, resp, info):
self.body = None
if resp:
self.body = resp.read()
self.info = info
@property
def json(self):
if not self.body:
if "body" in self.info:
return json.loads(self.info["body"])
return None
try:
return json.loads(self.body)
except ValueError:
return None
@property
def status_code(self):
return self.info["status"]
class Rest(object):
def __init__(self, module, headers):
self.module = module
self.headers = headers
self.baseurl = 'https://api.digitalocean.com/v2'
def _url_builder(self, path):
if path[0] == '/':
path = path[1:]
return '%s/%s' % (self.baseurl, path)
def send(self, method, path, data=None, headers=None):
url = self._url_builder(path)
data = self.module.jsonify(data)
timeout = self.module.params['timeout']
resp, info = fetch_url(self.module, url, data=data, headers=self.headers, method=method, timeout=timeout)
# Exceptions in fetch_url may result in a status -1, the ensures a
if info['status'] == -1:
self.module.fail_json(msg=info['msg'])
return Response(resp, info)
def get(self, path, data=None, headers=None):
return self.send('GET', path, data, headers)
def put(self, path, data=None, headers=None):
return self.send('PUT', path, data, headers)
def post(self, path, data=None, headers=None):
return self.send('POST', path, data, headers)
def delete(self, path, data=None, headers=None):
return self.send('DELETE', path, data, headers)
def core(module):
api_token = module.params['oauth_token']
state = module.params['state']
fingerprint = module.params['fingerprint']
name = module.params['name']
ssh_pub_key = module.params['ssh_pub_key']
rest = Rest(module, {'Authorization': 'Bearer {0}'.format(api_token),
'Content-type': 'application/json'})
fingerprint = fingerprint or ssh_key_fingerprint(ssh_pub_key)
response = rest.get('account/keys/{0}'.format(fingerprint))
status_code = response.status_code
json = response.json
if status_code not in (200, 404):
module.fail_json(msg='Error getting ssh key [{0}: {1}]'.format(
status_code, response.json['message']), fingerprint=fingerprint)
if state in ('present'):
if status_code == 404:
# IF key not found create it!
if module.check_mode:
module.exit_json(changed=True)
payload = {
'name': name,
'public_key': ssh_pub_key
}
response = rest.post('account/keys', data=payload)
status_code = response.status_code
json = response.json
if status_code == 201:
module.exit_json(changed=True, data=json)
module.fail_json(msg='Error creating ssh key [{0}: {1}]'.format(
status_code, response.json['message']))
elif status_code == 200:
# If key found was found, check if name needs to be updated
if name is None or json['ssh_key']['name'] == name:
module.exit_json(changed=False, data=json)
if module.check_mode:
module.exit_json(changed=True)
payload = {
'name': name,
}
response = rest.put('account/keys/{0}'.format(fingerprint), data=payload)
status_code = response.status_code
json = response.json
if status_code == 200:
module.exit_json(changed=True, data=json)
module.fail_json(msg='Error updating ssh key name [{0}: {1}]'.format(
status_code, response.json['message']), fingerprint=fingerprint)
elif state in ('absent'):
if status_code == 404:
module.exit_json(changed=False)
if module.check_mode:
module.exit_json(changed=True)
response = rest.delete('account/keys/{0}'.format(fingerprint))
status_code = response.status_code
json = response.json
if status_code == 204:
module.exit_json(changed=True)
module.fail_json(msg='Error creating ssh key [{0}: {1}]'.format(
status_code, response.json['message']))
def ssh_key_fingerprint(ssh_pub_key):
key = ssh_pub_key.split(None, 2)[1]
fingerprint = hashlib.md5(base64.b64decode(key)).hexdigest()
return ':'.join(a + b for a, b in zip(fingerprint[::2], fingerprint[1::2]))
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(choices=['present', 'absent'], default='present'),
fingerprint=dict(aliases=['id'], required=False),
name=dict(required=False),
ssh_pub_key=dict(required=False),
oauth_token=dict(
no_log=True,
# Support environment variable for DigitalOcean OAuth Token
fallback=(env_fallback, ['DO_API_TOKEN', 'DO_API_KEY', 'DO_OAUTH_TOKEN']),
required=True,
),
validate_certs=dict(type='bool', default=True),
timeout=dict(type='int', default=30),
),
required_one_of=(
('fingerprint', 'ssh_pub_key'),
),
supports_check_mode=True,
)
core(module)
if __name__ == '__main__':
main()

View file

@ -1,101 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Project
# 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: digital_ocean_sshkey_facts
deprecated:
removed_in: 3.0.0 # was Ansible 2.13
why: Deprecated in favour of C(_info) module.
alternative: Use M(community.general.digital_ocean_sshkey_info) instead.
short_description: DigitalOcean SSH keys facts
description:
- Fetch DigitalOcean SSH keys facts.
author: "Patrick Marques (@pmarques)"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Version 2 of DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- digital_ocean_sshkey_facts:
oauth_token: "{{ my_do_key }}"
- set_fact:
pubkey: "{{ item.public_key }}"
loop: "{{ ssh_keys|json_query(ssh_pubkey) }}"
vars:
ssh_pubkey: "[?name=='ansible_ctrl']"
- debug:
msg: "{{ pubkey }}"
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#list-all-keys
data:
description: List of SSH keys on DigitalOcean
returned: success and no resource constraint
type: dict
sample: {
"ssh_keys": [
{
"id": 512189,
"fingerprint": "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example",
"name": "My SSH Public Key"
}
],
"links": {
},
"meta": {
"total": 1
}
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
def core(module):
rest = DigitalOceanHelper(module)
response = rest.get("account/keys")
status_code = response.status_code
json = response.json
if status_code == 200:
module.exit_json(changed=False, ansible_facts=json)
else:
module.fail_json(msg='Error fetching facts [{0}: {1}]'.format(
status_code, response.json['message']))
def main():
module = AnsibleModule(
argument_spec=DigitalOceanHelper.digital_ocean_argument_spec(),
supports_check_mode=False,
)
module.deprecate("The 'digital_ocean_sshkey_facts' module has been deprecated, use the new 'digital_ocean_sshkey_info' module",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
core(module)
if __name__ == '__main__':
main()

View file

@ -1,92 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: Ansible Project
# 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: digital_ocean_sshkey_info
short_description: Gather information about DigitalOcean SSH keys
description:
- This module can be used to gather information about DigitalOcean SSH keys.
- This module replaces the C(digital_ocean_sshkey_facts) module.
author: "Patrick Marques (@pmarques)"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Version 2 of DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: Gather information about DigitalOcean SSH keys
digital_ocean_sshkey_info:
oauth_token: "{{ my_do_key }}"
register: ssh_keys
- name: Set facts based on the gathered information
set_fact:
pubkey: "{{ item.public_key }}"
loop: "{{ ssh_keys.data|json_query(ssh_pubkey) }}"
vars:
ssh_pubkey: "[?name=='ansible_ctrl']"
- name: Print SSH public key
debug:
msg: "{{ pubkey }}"
'''
RETURN = '''
# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#list-all-keys
data:
description: List of SSH keys on DigitalOcean
returned: success and no resource constraint
type: dict
sample: [
{
"id": 512189,
"fingerprint": "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example",
"name": "My SSH Public Key"
}
]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
def core(module):
rest = DigitalOceanHelper(module)
response = rest.get("account/keys")
status_code = response.status_code
json = response.json
if status_code == 200:
module.exit_json(changed=False, data=json['ssh_keys'])
else:
module.fail_json(msg='Error fetching SSH Key information [{0}: {1}]'.format(
status_code, response.json['message']))
def main():
module = AnsibleModule(
argument_spec=DigitalOceanHelper.digital_ocean_argument_spec(),
supports_check_mode=True,
)
core(module)
if __name__ == '__main__':
main()

View file

@ -1,205 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# 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: digital_ocean_tag
short_description: Create and remove tag(s) to DigitalOcean resource.
description:
- Create and remove tag(s) to DigitalOcean resource.
author: "Victor Volle (@kontrafiktion)"
options:
name:
description:
- The name of the tag. The supported characters for names include
alphanumeric characters, dashes, and underscores.
required: true
resource_id:
description:
- The ID of the resource to operate on.
- The data type of resource_id is changed from integer to string since Ansible 2.5.
aliases: ['droplet_id']
resource_type:
description:
- The type of resource to operate on. Currently, only tagging of
droplets is supported.
default: droplet
choices: ['droplet']
state:
description:
- Whether the tag should be present or absent on the resource.
default: present
choices: ['present', 'absent']
extends_documentation_fragment:
- community.general.digital_ocean.documentation
notes:
- Two environment variables can be used, DO_API_KEY and DO_API_TOKEN.
They both refer to the v2 token.
- As of Ansible 2.0, Version 2 of the DigitalOcean API is used.
requirements:
- "python >= 2.6"
'''
EXAMPLES = '''
- name: Create a tag
digital_ocean_tag:
name: production
state: present
- name: Tag a resource; creating the tag if it does not exist
digital_ocean_tag:
name: "{{ item }}"
resource_id: "73333005"
state: present
loop:
- staging
- dbserver
- name: Untag a resource
digital_ocean_tag:
name: staging
resource_id: "73333005"
state: absent
# Deleting a tag also untags all the resources that have previously been
# tagged with it
- name: Remove a tag
digital_ocean_tag:
name: dbserver
state: absent
'''
RETURN = '''
data:
description: a DigitalOcean Tag resource
returned: success and no resource constraint
type: dict
sample: {
"tag": {
"name": "awesome",
"resources": {
"droplets": {
"count": 0,
"last_tagged": null
}
}
}
}
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
state = module.params['state']
name = module.params['name']
resource_id = module.params['resource_id']
resource_type = module.params['resource_type']
rest = DigitalOceanHelper(module)
if state == 'present':
response = rest.get('tags/{0}'.format(name))
status_code = response.status_code
resp_json = response.json
changed = False
if status_code == 200 and resp_json['tag']['name'] == name:
changed = False
else:
# Ensure Tag exists
response = rest.post("tags", data={'name': name})
status_code = response.status_code
resp_json = response.json
if status_code == 201:
changed = True
elif status_code == 422:
changed = False
else:
module.exit_json(changed=False, data=resp_json)
if resource_id is None:
# No resource defined, we're done.
module.exit_json(changed=changed, data=resp_json)
else:
# Check if resource is already tagged or not
found = False
url = "{0}?tag_name={1}".format(resource_type, name)
if resource_type == 'droplet':
url = "droplets?tag_name={0}".format(name)
response = rest.get(url)
status_code = response.status_code
resp_json = response.json
if status_code == 200:
for resource in resp_json['droplets']:
if not found and resource['id'] == int(resource_id):
found = True
break
if not found:
# If resource is not tagged, tag a resource
url = "tags/{0}/resources".format(name)
payload = {
'resources': [{
'resource_id': resource_id,
'resource_type': resource_type}]}
response = rest.post(url, data=payload)
if response.status_code == 204:
module.exit_json(changed=True)
else:
module.fail_json(msg="error tagging resource '{0}': {1}".format(resource_id, response.json["message"]))
else:
# Already tagged resource
module.exit_json(changed=False)
else:
# Unable to find resource specified by user
module.fail_json(msg=resp_json['message'])
elif state == 'absent':
if resource_id:
url = "tags/{0}/resources".format(name)
payload = {
'resources': [{
'resource_id': resource_id,
'resource_type': resource_type}]}
response = rest.delete(url, data=payload)
else:
url = "tags/{0}".format(name)
response = rest.delete(url)
if response.status_code == 204:
module.exit_json(changed=True)
else:
module.exit_json(changed=False, data=response.json)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
name=dict(type='str', required=True),
resource_id=dict(aliases=['droplet_id'], type='str'),
resource_type=dict(choices=['droplet'], default='droplet'),
state=dict(choices=['present', 'absent'], default='present'),
)
module = AnsibleModule(argument_spec=argument_spec)
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_tag_info.py

View file

@ -1,115 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_tag_info
short_description: Gather information about DigitalOcean tags
description:
- This module can be used to gather information about DigitalOcean provided tags.
- This module was called C(digital_ocean_tag_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
tag_name:
description:
- Tag name that can be used to identify and reference a tag.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all tags
digital_ocean_tag_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about tag with given name
digital_ocean_tag_info:
oauth_token: "{{ oauth_token }}"
tag_name: "extra_awesome_tag"
- name: Get resources from tag name
digital_ocean_tag_info:
register: resp_out
- set_fact:
resources: "{{ item.resources }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?name=='extra_awesome_tag']"
- debug: var=resources
'''
RETURN = '''
data:
description: DigitalOcean tag information
returned: success
type: list
sample: [
{
"name": "extra-awesome",
"resources": {
"droplets": {
"count": 1,
...
}
}
},
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
tag_name = module.params.get('tag_name', None)
rest = DigitalOceanHelper(module)
base_url = 'tags'
if tag_name is not None:
response = rest.get("%s/%s" % (base_url, tag_name))
status_code = response.status_code
if status_code != 200:
module.fail_json(msg="Failed to retrieve tags for DigitalOcean")
resp_json = response.json
tag = resp_json['tag']
else:
tag = rest.get_paginated_data(base_url=base_url + '?', data_key_name='tags')
module.exit_json(changed=False, data=tag)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
tag_name=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_tag_facts', 'community.general.digital_ocean_tag_facts'):
module.deprecate("The 'digital_ocean_tag_facts' module has been renamed to 'digital_ocean_tag_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
digital_ocean_volume_info.py

View file

@ -1,140 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
# 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: digital_ocean_volume_info
short_description: Gather information about DigitalOcean volumes
description:
- This module can be used to gather information about DigitalOcean provided volumes.
- This module was called C(digital_ocean_volume_facts) before Ansible 2.9. The usage did not change.
author: "Abhijeet Kasurde (@Akasurde)"
options:
region_name:
description:
- Name of region to restrict results to volumes available in a specific region.
- Please use M(community.general.digital_ocean_region_info) for getting valid values related regions.
required: false
requirements:
- "python >= 2.6"
extends_documentation_fragment:
- community.general.digital_ocean.documentation
'''
EXAMPLES = '''
- name: Gather information about all volume
digital_ocean_volume_info:
oauth_token: "{{ oauth_token }}"
- name: Gather information about volume in given region
digital_ocean_volume_info:
region_name: nyc1
oauth_token: "{{ oauth_token }}"
- name: Get information about volume named nyc3-test-volume
digital_ocean_volume_info:
register: resp_out
- set_fact:
volume_id: "{{ item.id }}"
loop: "{{ resp_out.data|json_query(name) }}"
vars:
name: "[?name=='nyc3-test-volume']"
- debug: var=volume_id
'''
RETURN = '''
data:
description: DigitalOcean volume information
returned: success
type: list
sample: [
{
"id": "506f78a4-e098-11e5-ad9f-000f53306ae1",
"region": {
"name": "New York 1",
"slug": "nyc1",
"sizes": [
"s-1vcpu-1gb",
"s-1vcpu-2gb",
"s-1vcpu-3gb",
"s-2vcpu-2gb",
"s-3vcpu-1gb",
"s-2vcpu-4gb",
"s-4vcpu-8gb",
"s-6vcpu-16gb",
"s-8vcpu-32gb",
"s-12vcpu-48gb",
"s-16vcpu-64gb",
"s-20vcpu-96gb",
"s-24vcpu-128gb",
"s-32vcpu-192gb"
],
"features": [
"private_networking",
"backups",
"ipv6",
"metadata"
],
"available": true
},
"droplet_ids": [
],
"name": "example",
"description": "Block store for examples",
"size_gigabytes": 10,
"created_at": "2016-03-02T17:00:49Z"
}
]
'''
from traceback import format_exc
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.digital_ocean import DigitalOceanHelper
from ansible.module_utils._text import to_native
def core(module):
region_name = module.params.get('region_name', None)
rest = DigitalOceanHelper(module)
base_url = 'volumes?'
if region_name is not None:
base_url += "region=%s&" % region_name
volumes = rest.get_paginated_data(base_url=base_url, data_key_name='volumes')
module.exit_json(changed=False, data=volumes)
def main():
argument_spec = DigitalOceanHelper.digital_ocean_argument_spec()
argument_spec.update(
region_name=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec)
if module._name in ('digital_ocean_volume_facts', 'community.general.digital_ocean_volume_facts'):
module.deprecate("The 'digital_ocean_volume_facts' module has been renamed to 'digital_ocean_volume_info'",
version='3.0.0', collection_name='community.general') # was Ansible 2.13
try:
core(module)
except Exception as e:
module.fail_json(msg=to_native(e), exception=format_exc())
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_account_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_account_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_block_storage.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_certificate.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_certificate_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_certificate_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_domain.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_domain_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_domain_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_droplet.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_firewall_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_firewall_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_floating_ip.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_floating_ip_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_floating_ip_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_image_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_image_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_load_balancer_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_load_balancer_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_region_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_region_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_size_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_size_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_snapshot_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_snapshot_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_sshkey.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_sshkey_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_sshkey_info.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_tag.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_tag_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_tag_info.py

View file

@ -1 +0,0 @@
cloud/digital_ocean/digital_ocean_volume_facts.py

View file

@ -1 +0,0 @@
./cloud/digital_ocean/digital_ocean_volume_info.py

View file

@ -1,34 +0,0 @@
# Ansible DigitalOcean external inventory script settings
#
[digital_ocean]
# The module needs your DigitalOcean API Token.
# It may also be specified on the command line via --api-token
# or via the environment variables DO_API_TOKEN or DO_API_KEY
#
#api_token = 123456abcdefg
# API calls to DigitalOcean may be slow. For this reason, we cache the results
# of an API call. Set this to the path you want cache files to be written to.
# One file will be written to this directory:
# - ansible-digital_ocean.cache
#
cache_path = /tmp
# The number of seconds a cache file is considered valid. After this many
# seconds, a new API call will be made, and the cache file will be updated.
#
cache_max_age = 300
# Use the private network IP address instead of the public when available.
#
use_private_network = False
# Pass variables to every group, e.g.:
#
# group_variables = { 'ansible_user': 'root' }
#
group_variables = {}

View file

@ -1,541 +0,0 @@
#!/usr/bin/env python
"""
DigitalOcean external inventory script
======================================
Generates Ansible inventory of DigitalOcean Droplets.
In addition to the --list and --host options used by Ansible, there are options
for generating JSON of other DigitalOcean data. This is useful when creating
droplets. For example, --regions will return all the DigitalOcean Regions.
This information can also be easily found in the cache file, whose default
location is /tmp/ansible-digital_ocean.cache).
The --pretty (-p) option pretty-prints the output for better human readability.
----
Although the cache stores all the information received from DigitalOcean,
the cache is not used for current droplet information (in --list, --host,
--all, and --droplets). This is so that accurate droplet information is always
found. You can force this script to use the cache with --force-cache.
----
Configuration is read from `digital_ocean.ini`, then from environment variables,
and then from command-line arguments.
Most notably, the DigitalOcean API Token must be specified. It can be specified
in the INI file or with the following environment variables:
export DO_API_TOKEN='abc123' or
export DO_API_KEY='abc123'
Alternatively, it can be passed on the command-line with --api-token.
If you specify DigitalOcean credentials in the INI file, a handy way to
get them into your environment (e.g., to use the digital_ocean module)
is to use the output of the --env option with export:
export $(digital_ocean.py --env)
----
The following groups are generated from --list:
- ID (droplet ID)
- NAME (droplet NAME)
- digital_ocean
- image_ID
- image_NAME
- distro_NAME (distribution NAME from image)
- region_NAME
- size_NAME
- status_STATUS
For each host, the following variables are registered:
- do_backup_ids
- do_created_at
- do_disk
- do_features - list
- do_id
- do_image - object
- do_ip_address
- do_private_ip_address
- do_kernel - object
- do_locked
- do_memory
- do_name
- do_networks - object
- do_next_backup_window
- do_region - object
- do_size - object
- do_size_slug
- do_snapshot_ids - list
- do_status
- do_tags
- do_vcpus
- do_volume_ids
-----
```
usage: digital_ocean.py [-h] [--list] [--host HOST] [--all] [--droplets]
[--regions] [--images] [--sizes] [--ssh-keys]
[--domains] [--tags] [--pretty]
[--cache-path CACHE_PATH]
[--cache-max_age CACHE_MAX_AGE] [--force-cache]
[--refresh-cache] [--env] [--api-token API_TOKEN]
Produce an Ansible Inventory file based on DigitalOcean credentials
optional arguments:
-h, --help show this help message and exit
--list List all active Droplets as Ansible inventory
(default: True)
--host HOST Get all Ansible inventory variables about a specific
Droplet
--all List all DigitalOcean information as JSON
--droplets, -d List Droplets as JSON
--regions List Regions as JSON
--images List Images as JSON
--sizes List Sizes as JSON
--ssh-keys List SSH keys as JSON
--domains List Domains as JSON
--tags List Tags as JSON
--pretty, -p Pretty-print results
--cache-path CACHE_PATH
Path to the cache files (default: .)
--cache-max_age CACHE_MAX_AGE
Maximum age of the cached items (default: 0)
--force-cache Only use data from the cache
--refresh-cache, -r Force refresh of cache by making API requests to
DigitalOcean (default: False - use cache files)
--env, -e Display DO_API_TOKEN
--api-token API_TOKEN, -a API_TOKEN
DigitalOcean API Token
```
"""
# (c) 2013, Evan Wies <evan@neomantra.net>
# (c) 2017, Ansible Project
# (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
#
# Inspired by the EC2 inventory plugin:
# https://github.com/ansible/ansible/blob/devel/contrib/inventory/ec2.py
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
######################################################################
import argparse
import ast
import os
import re
import requests
import sys
from time import time
try:
import ConfigParser
except ImportError:
import configparser as ConfigParser
import json
class DoManager:
def __init__(self, api_token):
self.api_token = api_token
self.api_endpoint = 'https://api.digitalocean.com/v2'
self.headers = {'Authorization': 'Bearer {0}'.format(self.api_token),
'Content-type': 'application/json'}
self.timeout = 60
def _url_builder(self, path):
if path[0] == '/':
path = path[1:]
return '%s/%s' % (self.api_endpoint, path)
def send(self, url, method='GET', data=None):
url = self._url_builder(url)
data = json.dumps(data)
try:
if method == 'GET':
resp_data = {}
incomplete = True
while incomplete:
resp = requests.get(url, data=data, headers=self.headers, timeout=self.timeout)
json_resp = resp.json()
for key, value in json_resp.items():
if isinstance(value, list) and key in resp_data:
resp_data[key] += value
else:
resp_data[key] = value
try:
url = json_resp['links']['pages']['next']
except KeyError:
incomplete = False
except ValueError as e:
sys.exit("Unable to parse result from %s: %s" % (url, e))
return resp_data
def all_active_droplets(self):
resp = self.send('droplets/')
return resp['droplets']
def all_regions(self):
resp = self.send('regions/')
return resp['regions']
def all_images(self, filter_name='global'):
params = {'filter': filter_name}
resp = self.send('images/', data=params)
return resp['images']
def sizes(self):
resp = self.send('sizes/')
return resp['sizes']
def all_ssh_keys(self):
resp = self.send('account/keys')
return resp['ssh_keys']
def all_domains(self):
resp = self.send('domains/')
return resp['domains']
def show_droplet(self, droplet_id):
resp = self.send('droplets/%s' % droplet_id)
return resp['droplet']
def all_tags(self):
resp = self.send('tags')
return resp['tags']
class DigitalOceanInventory(object):
###########################################################################
# Main execution path
###########################################################################
def __init__(self):
"""Main execution path """
# DigitalOceanInventory data
self.data = {} # All DigitalOcean data
self.inventory = {} # Ansible Inventory
# Define defaults
self.cache_path = '.'
self.cache_max_age = 0
self.use_private_network = False
self.group_variables = {}
# Read settings, environment variables, and CLI arguments
self.read_settings()
self.read_environment()
self.read_cli_args()
# Verify credentials were set
if not hasattr(self, 'api_token'):
msg = 'Could not find values for DigitalOcean api_token. They must be specified via either ini file, ' \
'command line argument (--api-token), or environment variables (DO_API_TOKEN)\n'
sys.stderr.write(msg)
sys.exit(-1)
# env command, show DigitalOcean credentials
if self.args.env:
print("DO_API_TOKEN=%s" % self.api_token)
sys.exit(0)
# Manage cache
self.cache_filename = self.cache_path + "/ansible-digital_ocean.cache"
self.cache_refreshed = False
if self.is_cache_valid():
self.load_from_cache()
if len(self.data) == 0:
if self.args.force_cache:
sys.stderr.write('Cache is empty and --force-cache was specified\n')
sys.exit(-1)
self.manager = DoManager(self.api_token)
# Pick the json_data to print based on the CLI command
if self.args.droplets:
self.load_from_digital_ocean('droplets')
json_data = {'droplets': self.data['droplets']}
elif self.args.regions:
self.load_from_digital_ocean('regions')
json_data = {'regions': self.data['regions']}
elif self.args.images:
self.load_from_digital_ocean('images')
json_data = {'images': self.data['images']}
elif self.args.sizes:
self.load_from_digital_ocean('sizes')
json_data = {'sizes': self.data['sizes']}
elif self.args.ssh_keys:
self.load_from_digital_ocean('ssh_keys')
json_data = {'ssh_keys': self.data['ssh_keys']}
elif self.args.domains:
self.load_from_digital_ocean('domains')
json_data = {'domains': self.data['domains']}
elif self.args.tags:
self.load_from_digital_ocean('tags')
json_data = {'tags': self.data['tags']}
elif self.args.all:
self.load_from_digital_ocean()
json_data = self.data
elif self.args.host:
json_data = self.load_droplet_variables_for_host()
else: # '--list' this is last to make it default
self.load_from_digital_ocean('droplets')
self.build_inventory()
json_data = self.inventory
if self.cache_refreshed:
self.write_to_cache()
if self.args.pretty:
print(json.dumps(json_data, indent=2))
else:
print(json.dumps(json_data))
###########################################################################
# Script configuration
###########################################################################
def read_settings(self):
""" Reads the settings from the digital_ocean.ini file """
config = ConfigParser.ConfigParser()
config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'digital_ocean.ini')
config.read(config_path)
# Credentials
if config.has_option('digital_ocean', 'api_token'):
self.api_token = config.get('digital_ocean', 'api_token')
# Cache related
if config.has_option('digital_ocean', 'cache_path'):
self.cache_path = config.get('digital_ocean', 'cache_path')
if config.has_option('digital_ocean', 'cache_max_age'):
self.cache_max_age = config.getint('digital_ocean', 'cache_max_age')
# Private IP Address
if config.has_option('digital_ocean', 'use_private_network'):
self.use_private_network = config.getboolean('digital_ocean', 'use_private_network')
# Group variables
if config.has_option('digital_ocean', 'group_variables'):
self.group_variables = ast.literal_eval(config.get('digital_ocean', 'group_variables'))
def read_environment(self):
""" Reads the settings from environment variables """
# Setup credentials
if os.getenv("DO_API_TOKEN"):
self.api_token = os.getenv("DO_API_TOKEN")
if os.getenv("DO_API_KEY"):
self.api_token = os.getenv("DO_API_KEY")
def read_cli_args(self):
""" Command line argument processing """
parser = argparse.ArgumentParser(description='Produce an Ansible Inventory file based on DigitalOcean credentials')
parser.add_argument('--list', action='store_true', help='List all active Droplets as Ansible inventory (default: True)')
parser.add_argument('--host', action='store', help='Get all Ansible inventory variables about a specific Droplet')
parser.add_argument('--all', action='store_true', help='List all DigitalOcean information as JSON')
parser.add_argument('--droplets', '-d', action='store_true', help='List Droplets as JSON')
parser.add_argument('--regions', action='store_true', help='List Regions as JSON')
parser.add_argument('--images', action='store_true', help='List Images as JSON')
parser.add_argument('--sizes', action='store_true', help='List Sizes as JSON')
parser.add_argument('--ssh-keys', action='store_true', help='List SSH keys as JSON')
parser.add_argument('--domains', action='store_true', help='List Domains as JSON')
parser.add_argument('--tags', action='store_true', help='List Tags as JSON')
parser.add_argument('--pretty', '-p', action='store_true', help='Pretty-print results')
parser.add_argument('--cache-path', action='store', help='Path to the cache files (default: .)')
parser.add_argument('--cache-max_age', action='store', help='Maximum age of the cached items (default: 0)')
parser.add_argument('--force-cache', action='store_true', default=False, help='Only use data from the cache')
parser.add_argument('--refresh-cache', '-r', action='store_true', default=False,
help='Force refresh of cache by making API requests to DigitalOcean (default: False - use cache files)')
parser.add_argument('--env', '-e', action='store_true', help='Display DO_API_TOKEN')
parser.add_argument('--api-token', '-a', action='store', help='DigitalOcean API Token')
self.args = parser.parse_args()
if self.args.api_token:
self.api_token = self.args.api_token
# Make --list default if none of the other commands are specified
if (not self.args.droplets and not self.args.regions and
not self.args.images and not self.args.sizes and
not self.args.ssh_keys and not self.args.domains and
not self.args.tags and
not self.args.all and not self.args.host):
self.args.list = True
###########################################################################
# Data Management
###########################################################################
def load_from_digital_ocean(self, resource=None):
"""Get JSON from DigitalOcean API """
if self.args.force_cache and os.path.isfile(self.cache_filename):
return
# We always get fresh droplets
if self.is_cache_valid() and not (resource == 'droplets' or resource is None):
return
if self.args.refresh_cache:
resource = None
if resource == 'droplets' or resource is None:
self.data['droplets'] = self.manager.all_active_droplets()
self.cache_refreshed = True
if resource == 'regions' or resource is None:
self.data['regions'] = self.manager.all_regions()
self.cache_refreshed = True
if resource == 'images' or resource is None:
self.data['images'] = self.manager.all_images()
self.cache_refreshed = True
if resource == 'sizes' or resource is None:
self.data['sizes'] = self.manager.sizes()
self.cache_refreshed = True
if resource == 'ssh_keys' or resource is None:
self.data['ssh_keys'] = self.manager.all_ssh_keys()
self.cache_refreshed = True
if resource == 'domains' or resource is None:
self.data['domains'] = self.manager.all_domains()
self.cache_refreshed = True
if resource == 'tags' or resource is None:
self.data['tags'] = self.manager.all_tags()
self.cache_refreshed = True
def add_inventory_group(self, key):
""" Method to create group dict """
host_dict = {'hosts': [], 'vars': {}}
self.inventory[key] = host_dict
return
def add_host(self, group, host):
""" Helper method to reduce host duplication """
if group not in self.inventory:
self.add_inventory_group(group)
if host not in self.inventory[group]['hosts']:
self.inventory[group]['hosts'].append(host)
return
def build_inventory(self):
""" Build Ansible inventory of droplets """
self.inventory = {
'all': {
'hosts': [],
'vars': self.group_variables
},
'_meta': {'hostvars': {}}
}
# add all droplets by id and name
for droplet in self.data['droplets']:
for net in droplet['networks']['v4']:
if net['type'] == 'public':
dest = net['ip_address']
else:
continue
self.inventory['all']['hosts'].append(dest)
self.add_host(droplet['id'], dest)
self.add_host(droplet['name'], dest)
# groups that are always present
for group in ('digital_ocean',
'region_' + droplet['region']['slug'],
'image_' + str(droplet['image']['id']),
'size_' + droplet['size']['slug'],
'distro_' + DigitalOceanInventory.to_safe(droplet['image']['distribution']),
'status_' + droplet['status']):
self.add_host(group, dest)
# groups that are not always present
for group in (droplet['image']['slug'],
droplet['image']['name']):
if group:
image = 'image_' + DigitalOceanInventory.to_safe(group)
self.add_host(image, dest)
if droplet['tags']:
for tag in droplet['tags']:
self.add_host(tag, dest)
# hostvars
info = self.do_namespace(droplet)
self.inventory['_meta']['hostvars'][dest] = info
def load_droplet_variables_for_host(self):
""" Generate a JSON response to a --host call """
host = int(self.args.host)
droplet = self.manager.show_droplet(host)
info = self.do_namespace(droplet)
return {'droplet': info}
###########################################################################
# Cache Management
###########################################################################
def is_cache_valid(self):
""" Determines if the cache files have expired, or if it is still valid """
if os.path.isfile(self.cache_filename):
mod_time = os.path.getmtime(self.cache_filename)
current_time = time()
if (mod_time + self.cache_max_age) > current_time:
return True
return False
def load_from_cache(self):
""" Reads the data from the cache file and assigns it to member variables as Python Objects """
try:
with open(self.cache_filename, 'r') as cache:
json_data = cache.read()
data = json.loads(json_data)
except IOError:
data = {'data': {}, 'inventory': {}}
self.data = data['data']
self.inventory = data['inventory']
def write_to_cache(self):
""" Writes data in JSON format to a file """
data = {'data': self.data, 'inventory': self.inventory}
json_data = json.dumps(data, indent=2)
with open(self.cache_filename, 'w') as cache:
cache.write(json_data)
###########################################################################
# Utilities
###########################################################################
@staticmethod
def to_safe(word):
""" Converts 'bad' characters in a string to underscores so they can be used as Ansible groups """
return re.sub(r"[^A-Za-z0-9\-.]", "_", word)
@staticmethod
def do_namespace(data):
""" Returns a copy of the dictionary with all the keys put in a 'do_' namespace """
info = {}
for k, v in data.items():
info['do_' + k] = v
return info
###########################################################################
# Run the script
DigitalOceanInventory()

View file

@ -1 +0,0 @@
unsupported

View file

@ -1,42 +0,0 @@
---
- name: Test API key is provided.
fail:
msg: do_api_key should be defined in integration_config.yml
when: do_api_key is not defined
- name: "Make sure that the Floating IP is absent"
digital_ocean_floating_ip:
state: absent
ip: "8.8.8.8"
oauth_token: "{{ do_api_key }}"
register: result
- name: Verify that the Floating IP didn't change
assert:
that:
- "not result.changed"
- name: "Create a Floating IP"
digital_ocean_floating_ip:
state: present
region: "lon1"
oauth_token: "{{ do_api_key }}"
register: result
- name: Verify that a Floating IP was created
assert:
that:
- "result.changed"
- name: "Destroy Floating IP"
digital_ocean_floating_ip:
state: absent
ip: "{{ result.data.floating_ip.ip }}"
region: "lon1"
oauth_token: "{{ do_api_key }}"
register: result
- name: Verify that a Floating IP was deleted
assert:
that:
- " result.changed"

View file

@ -1 +0,0 @@
unsupported

View file

@ -1,29 +0,0 @@
---
- name: Test API key is provided.
fail:
msg: do_api_key should be defined in integration_config.yml
when: do_api_key is not defined
- name: Create ssh key
digital_ocean_sshkey:
name: test-key1
ssh_pub_key: "{{ dummy_ssh_pub_key }}"
oauth_token: "{{ do_api_key }}"
register: result
- name: Verify that SSH key was created
assert:
that:
- "result.changed"
- name: "Delete ssh key"
digital_ocean_sshkey:
state: "absent"
fingerprint: "{{ result.data.ssh_key.fingerprint }}"
oauth_token: "{{ do_api_key }}"
register: result
- name: Verify that SSH key was deleted
assert:
that:
- "result.changed"

View file

@ -1,2 +0,0 @@
digital_ocean
unsupported

View file

@ -1,51 +0,0 @@
---
- block:
- name: Test API key is provided.
fail:
msg: do_api_key should be defined in integration_config.yml
when: do_api_key is not defined
- name: Create a new tag
digital_ocean_tag:
oauth_token: '{{do_api_key}}'
name: integration-test
state: present
register: create_tag
- name: Create a new tag for idempotency
digital_ocean_tag:
oauth_token: '{{do_api_key}}'
name: integration-test
state: present
register: create_tag_idempotent
- debug:
var: create_tag
- assert:
that:
- create_tag.changed == True
- create_tag_idempotent.changed == False
- create_tag.data.tag.name == "integration-test"
always:
- name: Delete tag
digital_ocean_tag:
oauth_token: '{{do_api_key}}'
name: integration-test
state: absent
register: delete_tag
# FIXME: Deleting a tag isn't idempotent
# - name: Delete tag with idempotency
# digital_ocean_tag:
# oauth_token: '{{do_api_key}}'
# name: integration-test
# state: absent
# register: delete_tag_idempotent
- assert:
that:
- delete_tag.changed == True
# FIXME: Deleting a tag isn't idempotent
# - delete_tag_idempotent.changed == False

View file

@ -46,40 +46,6 @@ plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:doc-mi
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_firewall_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_image_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_load_balancer_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_snapshot_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_tag_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_volume_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc

View file

@ -46,40 +46,6 @@ plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:doc-mi
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:doc-required-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:parameter-list-no-elements
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_firewall_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_image_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_load_balancer_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_snapshot_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_tag_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_volume_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc

View file

@ -32,37 +32,6 @@ plugins/modules/cloud/centurylink/clc_server.py validate-modules:parameter-type-
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:doc-missing-type plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:doc-missing-type
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:implied-parameter-type-mismatch
plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/centurylink/clc_server_snapshot.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:deprecation-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:invalid-documentation
plugins/modules/cloud/digital_ocean/digital_ocean.py validate-modules:missing-main-call
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_block_storage.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_certificate.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_certificate_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_domain.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_domain_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_droplet.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_firewall_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_floating_ip.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_image_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_load_balancer_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_snapshot_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-default-does-not-match-spec
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey.py validate-modules:undocumented-parameter
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey_facts.py validate-modules:deprecation-mismatch
plugins/modules/cloud/digital_ocean/digital_ocean_sshkey_facts.py validate-modules:invalid-documentation
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:doc-missing-type
plugins/modules/cloud/digital_ocean/digital_ocean_tag.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_tag_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/digital_ocean/digital_ocean_volume_info.py validate-modules:parameter-type-not-in-doc
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-choices-do-not-match-spec
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:doc-missing-type
plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc plugins/modules/cloud/dimensiondata/dimensiondata_network.py validate-modules:parameter-type-not-in-doc