2018-04-25 17:35:54 +00:00
|
|
|
# Copyright (c) 2017 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 = '''
|
|
|
|
name: scaleway
|
|
|
|
plugin_type: inventory
|
2018-07-24 09:01:42 +00:00
|
|
|
author:
|
|
|
|
- Remy Leone (@sieben)
|
2018-04-25 17:35:54 +00:00
|
|
|
short_description: Scaleway inventory source
|
|
|
|
description:
|
|
|
|
- Get inventory hosts from Scaleway
|
|
|
|
options:
|
2018-07-05 17:01:16 +00:00
|
|
|
plugin:
|
|
|
|
description: token that ensures this is a source file for the 'scaleway' plugin.
|
|
|
|
required: True
|
|
|
|
choices: ['scaleway']
|
2018-04-25 17:35:54 +00:00
|
|
|
regions:
|
|
|
|
description: Filter results on a specific Scaleway region
|
|
|
|
type: list
|
|
|
|
default:
|
|
|
|
- ams1
|
|
|
|
- par1
|
|
|
|
tags:
|
|
|
|
description: Filter results on a specific tag
|
|
|
|
type: list
|
|
|
|
oauth_token:
|
2018-06-18 17:54:38 +00:00
|
|
|
required: True
|
2018-04-25 17:35:54 +00:00
|
|
|
description: Scaleway OAuth token.
|
|
|
|
env:
|
|
|
|
# in order of precedence
|
|
|
|
- name: SCW_TOKEN
|
|
|
|
- name: SCW_API_KEY
|
|
|
|
- name: SCW_OAUTH_TOKEN
|
2018-07-17 08:10:17 +00:00
|
|
|
hostnames:
|
|
|
|
description: List of preference about what to use as an hostname.
|
|
|
|
type: list
|
|
|
|
default:
|
|
|
|
- public_ipv4
|
|
|
|
choices:
|
|
|
|
- public_ipv4
|
|
|
|
- private_ipv4
|
|
|
|
- public_ipv6
|
|
|
|
- hostname
|
|
|
|
- id
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
variables:
|
|
|
|
description: 'set individual variables: keys are variable names and
|
|
|
|
values are templates. Any value returned by the
|
|
|
|
L(Scaleway API, https://developer.scaleway.com/#servers-server-get)
|
|
|
|
can be used.'
|
|
|
|
type: dict
|
2018-04-25 17:35:54 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
# scaleway_inventory.yml file in YAML format
|
|
|
|
# Example command line: ansible-inventory --list -i scaleway_inventory.yml
|
|
|
|
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
# use hostname as inventory_hostname
|
|
|
|
# use the private IP address to connect to the host
|
2018-04-25 17:35:54 +00:00
|
|
|
plugin: scaleway
|
|
|
|
regions:
|
|
|
|
- ams1
|
|
|
|
- par1
|
|
|
|
tags:
|
|
|
|
- foobar
|
2018-07-17 08:10:17 +00:00
|
|
|
hostnames:
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
- hostname
|
|
|
|
variables:
|
|
|
|
ansible_host: private_ip
|
|
|
|
state: state
|
|
|
|
|
|
|
|
# use hostname as inventory_hostname and public IP address to connect to the host
|
|
|
|
plugin: scaleway
|
|
|
|
hostnames:
|
|
|
|
- hostname
|
|
|
|
regions:
|
|
|
|
- par1
|
|
|
|
variables:
|
|
|
|
ansible_host: public_ip.address
|
2018-04-25 17:35:54 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
|
|
from ansible.errors import AnsibleError
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
2018-04-25 17:35:54 +00:00
|
|
|
from ansible.module_utils.scaleway import SCALEWAY_LOCATION
|
|
|
|
from ansible.module_utils.urls import open_url
|
2018-06-18 17:54:38 +00:00
|
|
|
from ansible.module_utils._text import to_native
|
2018-04-25 17:35:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _fetch_information(token, url):
|
|
|
|
try:
|
|
|
|
response = open_url(url,
|
|
|
|
headers={'X-Auth-Token': token,
|
|
|
|
'Content-type': 'application/json'})
|
2018-06-18 17:54:38 +00:00
|
|
|
except Exception as e:
|
|
|
|
raise AnsibleError("Error while fetching %s: %s" % (url, to_native(e)))
|
2018-04-25 17:35:54 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
raw_json = json.loads(response.read())
|
|
|
|
except ValueError:
|
|
|
|
raise AnsibleError("Incorrect JSON payload")
|
|
|
|
|
|
|
|
try:
|
|
|
|
return raw_json["servers"]
|
|
|
|
except KeyError:
|
|
|
|
raise AnsibleError("Incorrect format from the Scaleway API response")
|
|
|
|
|
|
|
|
|
|
|
|
def _build_server_url(api_endpoint):
|
|
|
|
return "/".join([api_endpoint, "servers"])
|
|
|
|
|
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
def extract_public_ipv4(server_info):
|
|
|
|
try:
|
|
|
|
return server_info["public_ip"]["address"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def extract_private_ipv4(server_info):
|
2018-07-19 18:03:34 +00:00
|
|
|
try:
|
|
|
|
return server_info["private_ip"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
2018-07-17 08:10:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def extract_hostname(server_info):
|
2018-07-19 18:03:34 +00:00
|
|
|
try:
|
|
|
|
return server_info["hostname"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
2018-07-17 08:10:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def extract_server_id(server_info):
|
2018-07-19 18:03:34 +00:00
|
|
|
try:
|
|
|
|
return server_info["id"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
2018-07-17 08:10:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
def extract_public_ipv6(server_info):
|
|
|
|
try:
|
|
|
|
return server_info["ipv6"]["address"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2018-07-19 18:03:34 +00:00
|
|
|
def extract_tags(server_info):
|
|
|
|
try:
|
|
|
|
return server_info["tags"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def extract_zone(server_info):
|
|
|
|
try:
|
|
|
|
return server_info["location"]["zone_id"]
|
|
|
|
except (KeyError, TypeError):
|
|
|
|
return None
|
|
|
|
|
2018-07-29 11:46:06 +00:00
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
extractors = {
|
|
|
|
"public_ipv4": extract_public_ipv4,
|
|
|
|
"private_ipv4": extract_private_ipv4,
|
|
|
|
"public_ipv6": extract_public_ipv6,
|
|
|
|
"hostname": extract_hostname,
|
|
|
|
"id": extract_server_id
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
class InventoryModule(BaseInventoryPlugin, Constructable):
|
2018-04-25 17:35:54 +00:00
|
|
|
NAME = 'scaleway'
|
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
def _fill_host_variables(self, host, server_info):
|
2018-04-25 17:35:54 +00:00
|
|
|
targeted_attributes = (
|
|
|
|
"arch",
|
|
|
|
"commercial_type",
|
2018-07-17 08:10:17 +00:00
|
|
|
"id",
|
2018-04-25 17:35:54 +00:00
|
|
|
"organization",
|
|
|
|
"state",
|
|
|
|
"hostname",
|
|
|
|
)
|
|
|
|
for attribute in targeted_attributes:
|
2018-07-17 08:10:17 +00:00
|
|
|
self.inventory.set_variable(host, attribute, server_info[attribute])
|
|
|
|
|
|
|
|
self.inventory.set_variable(host, "tags", server_info["tags"])
|
|
|
|
|
|
|
|
if extract_public_ipv6(server_info=server_info):
|
|
|
|
self.inventory.set_variable(host, "public_ipv6", extract_public_ipv6(server_info=server_info))
|
2018-04-25 17:35:54 +00:00
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
if extract_public_ipv4(server_info=server_info):
|
|
|
|
self.inventory.set_variable(host, "public_ipv4", extract_public_ipv4(server_info=server_info))
|
2018-04-25 17:35:54 +00:00
|
|
|
|
2018-08-28 20:35:38 +00:00
|
|
|
if extract_private_ipv4(server_info=server_info):
|
|
|
|
self.inventory.set_variable(host, "private_ipv4", extract_private_ipv4(server_info=server_info))
|
|
|
|
|
2018-06-18 17:54:38 +00:00
|
|
|
def _get_zones(self, config_zones):
|
2018-04-25 17:35:54 +00:00
|
|
|
return set(SCALEWAY_LOCATION.keys()).intersection(config_zones)
|
|
|
|
|
2018-06-18 17:54:38 +00:00
|
|
|
def match_groups(self, server_info, tags):
|
2018-07-19 18:03:34 +00:00
|
|
|
server_zone = extract_zone(server_info=server_info)
|
|
|
|
server_tags = extract_tags(server_info=server_info)
|
|
|
|
|
|
|
|
# If a server does not have a zone, it means it is archived
|
|
|
|
if server_zone is None:
|
|
|
|
return set()
|
2018-04-25 17:35:54 +00:00
|
|
|
|
|
|
|
# If no filtering is defined, all tags are valid groups
|
2018-06-18 17:54:38 +00:00
|
|
|
if tags is None:
|
2018-04-25 17:35:54 +00:00
|
|
|
return set(server_tags).union((server_zone,))
|
|
|
|
|
2018-06-18 17:54:38 +00:00
|
|
|
matching_tags = set(server_tags).intersection(tags)
|
2018-04-25 17:35:54 +00:00
|
|
|
|
|
|
|
if not matching_tags:
|
|
|
|
return set()
|
|
|
|
else:
|
|
|
|
return matching_tags.union((server_zone,))
|
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
def _filter_host(self, host_infos, hostname_preferences):
|
|
|
|
|
|
|
|
for pref in hostname_preferences:
|
|
|
|
if extractors[pref](host_infos):
|
|
|
|
return extractors[pref](host_infos)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
def do_zone_inventory(self, zone, token, tags, hostname_preferences):
|
2018-04-25 17:35:54 +00:00
|
|
|
self.inventory.add_group(zone)
|
|
|
|
zone_info = SCALEWAY_LOCATION[zone]
|
|
|
|
|
|
|
|
url = _build_server_url(zone_info["api_endpoint"])
|
2018-07-17 08:10:17 +00:00
|
|
|
raw_zone_hosts_infos = _fetch_information(url=url, token=token)
|
|
|
|
|
|
|
|
for host_infos in raw_zone_hosts_infos:
|
|
|
|
|
|
|
|
hostname = self._filter_host(host_infos=host_infos,
|
|
|
|
hostname_preferences=hostname_preferences)
|
2018-04-25 17:35:54 +00:00
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
# No suitable hostname were found in the attributes and the host won't be in the inventory
|
|
|
|
if not hostname:
|
|
|
|
continue
|
2018-04-25 17:35:54 +00:00
|
|
|
|
2018-07-17 08:10:17 +00:00
|
|
|
groups = self.match_groups(host_infos, tags)
|
2018-04-25 17:35:54 +00:00
|
|
|
|
|
|
|
for group in groups:
|
|
|
|
self.inventory.add_group(group=group)
|
2018-07-17 08:10:17 +00:00
|
|
|
self.inventory.add_host(group=group, host=hostname)
|
|
|
|
self._fill_host_variables(host=hostname, server_info=host_infos)
|
2018-04-25 17:35:54 +00:00
|
|
|
|
Scaleway inventory: allows to connect via private IP (#44342)
* scaleway inventory: remove useless duplicate
* scaleway inventory: allows to connect using private ip
ansible_host was hardcoded and it was not possible to connect using
private addresses.
This allows to define multiple host variables, values are templates
which can use hosts details send by API. For example this config file
use private address and defines two variables:
plugin: scaleway
hostnames:
- hostname
variables:
ansible_host: private_ip
state: state
image: image.name
regions:
- ams1
inventory will looks like:
{
"_meta": {
"hostvars": {
"testhost": {
"ansible_host": "10.1.1.1",
"arch": "x86_64",
"commercial_type": "START1-M",
"hostname": "testhost",
"id": "af669464-0c74-4c89-8573-9fe763028448",
"image": "CentOS 7.4",
"organization": "2cc9a115-380d-4ac0-ba4b-8947eee71325",
"public_ipv4": "163.172.1.1",
"public_ipv6": "2001:bc8::1",
"state": "running",
"tags": [
"testtag"
]
}
}
},
[...]
}
2018-08-29 16:14:12 +00:00
|
|
|
# Composed variables
|
|
|
|
self._set_composite_vars(self.get_option('variables'), host_infos, hostname, strict=False)
|
|
|
|
|
2018-04-25 17:35:54 +00:00
|
|
|
def parse(self, inventory, loader, path, cache=True):
|
|
|
|
super(InventoryModule, self).parse(inventory, loader, path)
|
2018-06-18 17:54:38 +00:00
|
|
|
self._read_config_data(path=path)
|
|
|
|
|
|
|
|
config_zones = self.get_option("regions")
|
|
|
|
tags = self.get_option("tags")
|
|
|
|
token = self.get_option("oauth_token")
|
2018-07-17 08:10:17 +00:00
|
|
|
hostname_preference = self.get_option("hostnames")
|
2018-04-25 17:35:54 +00:00
|
|
|
|
2018-06-18 17:54:38 +00:00
|
|
|
for zone in self._get_zones(config_zones):
|
2018-07-17 08:10:17 +00:00
|
|
|
self.do_zone_inventory(zone=zone, token=token, tags=tags, hostname_preferences=hostname_preference)
|