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:
parent
25aec0d712
commit
c1b5b51366
84 changed files with 154 additions and 5011 deletions
28
.github/BOTMETA.yml
vendored
28
.github/BOTMETA.yml
vendored
|
@ -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
|
||||||
|
|
2
changelogs/fragments/digital-ocean.yml
Normal file
2
changelogs/fragments/digital-ocean.yml
Normal 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)."
|
179
meta/runtime.yml
179
meta/runtime.yml
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
'''
|
|
|
@ -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
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_account_info.py
|
|
|
@ -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()
|
|
|
@ -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}®ion={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}®ion={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()
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_certificate_info.py
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_domain_info.py
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_firewall_info.py
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_floating_ip_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_image_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_load_balancer_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_region_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_size_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_snapshot_info.py
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_tag_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
digital_ocean_volume_info.py
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_account_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_account_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_block_storage.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_certificate.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_certificate_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_certificate_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_domain.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_domain_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_domain_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_droplet.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_firewall_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_firewall_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_floating_ip.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_floating_ip_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_floating_ip_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_image_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_image_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_load_balancer_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_load_balancer_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_region_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_region_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_size_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_size_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_snapshot_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_snapshot_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_sshkey.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_sshkey_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_sshkey_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_tag.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_tag_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_tag_info.py
|
|
|
@ -1 +0,0 @@
|
||||||
cloud/digital_ocean/digital_ocean_volume_facts.py
|
|
|
@ -1 +0,0 @@
|
||||||
./cloud/digital_ocean/digital_ocean_volume_info.py
|
|
|
@ -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 = {}
|
|
|
@ -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()
|
|
|
@ -1 +0,0 @@
|
||||||
unsupported
|
|
|
@ -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"
|
|
|
@ -1 +0,0 @@
|
||||||
unsupported
|
|
|
@ -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"
|
|
|
@ -1,2 +0,0 @@
|
||||||
digital_ocean
|
|
||||||
unsupported
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue