1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 07:51:50 +00:00

Reformat everything.

This commit is contained in:
Felix Fontein 2025-11-01 12:08:41 +01:00
parent 3f2213791a
commit 340ff8586d
1008 changed files with 61301 additions and 58309 deletions

View file

@ -140,12 +140,13 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
# xmlrpc
try:
import xmlrpc.client as xmlrpc_client
HAS_XMLRPC_CLIENT = True
except ImportError:
HAS_XMLRPC_CLIENT = False
class TimeoutTransport (xmlrpc_client.SafeTransport):
class TimeoutTransport(xmlrpc_client.SafeTransport):
def __init__(self, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
super().__init__()
self._timeout = timeout
@ -158,21 +159,21 @@ class TimeoutTransport (xmlrpc_client.SafeTransport):
class InventoryModule(BaseInventoryPlugin, Cacheable):
''' Host inventory parser for ansible using cobbler as source. '''
"""Host inventory parser for ansible using cobbler as source."""
NAME = 'community.general.cobbler'
NAME = "community.general.cobbler"
def __init__(self):
super().__init__()
self.cache_key = None
if not HAS_XMLRPC_CLIENT:
raise AnsibleError('Could not import xmlrpc client library')
raise AnsibleError("Could not import xmlrpc client library")
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('cobbler.yaml', 'cobbler.yml')):
if path.endswith(("cobbler.yaml", "cobbler.yml")):
valid = True
else:
self.display.vvv('Skipping due to inventory source not ending in "cobbler.yaml" nor "cobbler.yml"')
@ -183,14 +184,14 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
self._cache[self.cache_key] = {}
def _reload_cache(self):
if self.get_option('cache_fallback'):
self.display.vvv('Cannot connect to server, loading cache\n')
self._options['cache_timeout'] = 0
if self.get_option("cache_fallback"):
self.display.vvv("Cannot connect to server, loading cache\n")
self._options["cache_timeout"] = 0
self.load_cache_plugin()
self._cache.get(self.cache_key, {})
def _get_profiles(self):
if not self.use_cache or 'profiles' not in self._cache.get(self.cache_key, {}):
if not self.use_cache or "profiles" not in self._cache.get(self.cache_key, {}):
try:
if self.token is not None:
data = self.cobbler.get_profiles(self.token)
@ -200,12 +201,12 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
self._reload_cache()
else:
self._init_cache()
self._cache[self.cache_key]['profiles'] = data
self._cache[self.cache_key]["profiles"] = data
return self._cache[self.cache_key]['profiles']
return self._cache[self.cache_key]["profiles"]
def _get_systems(self):
if not self.use_cache or 'systems' not in self._cache.get(self.cache_key, {}):
if not self.use_cache or "systems" not in self._cache.get(self.cache_key, {}):
try:
if self.token is not None:
data = self.cobbler.get_systems(self.token)
@ -217,19 +218,21 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
for i, host in enumerate(data):
self.display.vvvv(f"Gathering all facts for {host['name']}\n")
if self.token is not None:
data[i] = self.cobbler.get_system_as_rendered(host['name'], self.token)
data[i] = self.cobbler.get_system_as_rendered(host["name"], self.token)
else:
data[i] = self.cobbler.get_system_as_rendered(host['name'])
data[i] = self.cobbler.get_system_as_rendered(host["name"])
except (socket.gaierror, socket.error, xmlrpc_client.ProtocolError):
self._reload_cache()
else:
self._init_cache()
self._cache[self.cache_key]['systems'] = data
self._cache[self.cache_key]["systems"] = data
return self._cache[self.cache_key]['systems']
return self._cache[self.cache_key]["systems"]
def _add_safe_group_name(self, group, child=None):
group_name = self.inventory.add_group(to_safe_group_name(f"{self.get_option('group_prefix')}{group.lower().replace(' ', '')}"))
group_name = self.inventory.add_group(
to_safe_group_name(f"{self.get_option('group_prefix')}{group.lower().replace(' ', '')}")
)
if child is not None:
self.inventory.add_child(group_name, child)
return group_name
@ -241,101 +244,103 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
return profile in self.exclude_profiles
def parse(self, inventory, loader, path, cache=True):
super().parse(inventory, loader, path)
# read config from file, this sets 'options'
self._read_config_data(path)
# get connection host
self.cobbler_url = self.get_option('url')
self.display.vvvv(f'Connecting to {self.cobbler_url}\n')
self.cobbler_url = self.get_option("url")
self.display.vvvv(f"Connecting to {self.cobbler_url}\n")
if 'connection_timeout' in self._options:
self.cobbler = xmlrpc_client.Server(self.cobbler_url, allow_none=True,
transport=TimeoutTransport(timeout=self.get_option('connection_timeout')))
if "connection_timeout" in self._options:
self.cobbler = xmlrpc_client.Server(
self.cobbler_url,
allow_none=True,
transport=TimeoutTransport(timeout=self.get_option("connection_timeout")),
)
else:
self.cobbler = xmlrpc_client.Server(self.cobbler_url, allow_none=True)
self.token = None
if self.get_option('user') is not None:
self.token = self.cobbler.login(str(self.get_option('user')), str(self.get_option('password')))
if self.get_option("user") is not None:
self.token = self.cobbler.login(str(self.get_option("user")), str(self.get_option("password")))
self.cache_key = self.get_cache_key(path)
self.use_cache = cache and self.get_option('cache')
self.use_cache = cache and self.get_option("cache")
self.exclude_mgmt_classes = self.get_option('exclude_mgmt_classes')
self.include_mgmt_classes = self.get_option('include_mgmt_classes')
self.exclude_profiles = self.get_option('exclude_profiles')
self.include_profiles = self.get_option('include_profiles')
self.group_by = self.get_option('group_by')
self.inventory_hostname = self.get_option('inventory_hostname')
self.facts_level = self.get_option('facts_level')
self.exclude_mgmt_classes = self.get_option("exclude_mgmt_classes")
self.include_mgmt_classes = self.get_option("include_mgmt_classes")
self.exclude_profiles = self.get_option("exclude_profiles")
self.include_profiles = self.get_option("include_profiles")
self.group_by = self.get_option("group_by")
self.inventory_hostname = self.get_option("inventory_hostname")
self.facts_level = self.get_option("facts_level")
for profile in self._get_profiles():
if profile['parent']:
if profile["parent"]:
self.display.vvvv(f"Processing profile {profile['name']} with parent {profile['parent']}\n")
if not self._exclude_profile(profile['parent']):
parent_group_name = self._add_safe_group_name(profile['parent'])
self.display.vvvv(f'Added profile parent group {parent_group_name}\n')
if not self._exclude_profile(profile['name']):
group_name = self._add_safe_group_name(profile['name'])
self.display.vvvv(f'Added profile group {group_name}\n')
if not self._exclude_profile(profile["parent"]):
parent_group_name = self._add_safe_group_name(profile["parent"])
self.display.vvvv(f"Added profile parent group {parent_group_name}\n")
if not self._exclude_profile(profile["name"]):
group_name = self._add_safe_group_name(profile["name"])
self.display.vvvv(f"Added profile group {group_name}\n")
self.inventory.add_child(parent_group_name, group_name)
else:
self.display.vvvv(f"Processing profile {profile['name']} without parent\n")
# Create a hierarchy of profile names
profile_elements = profile['name'].split('-')
profile_elements = profile["name"].split("-")
i = 0
while i < len(profile_elements) - 1:
profile_group = '-'.join(profile_elements[0:i + 1])
profile_group_child = '-'.join(profile_elements[0:i + 2])
profile_group = "-".join(profile_elements[0 : i + 1])
profile_group_child = "-".join(profile_elements[0 : i + 2])
if self._exclude_profile(profile_group):
self.display.vvvv(f'Excluding profile {profile_group}\n')
self.display.vvvv(f"Excluding profile {profile_group}\n")
break
group_name = self._add_safe_group_name(profile_group)
self.display.vvvv(f'Added profile group {group_name}\n')
self.display.vvvv(f"Added profile group {group_name}\n")
child_group_name = self._add_safe_group_name(profile_group_child)
self.display.vvvv(f'Added profile child group {child_group_name} to {group_name}\n')
self.display.vvvv(f"Added profile child group {child_group_name} to {group_name}\n")
self.inventory.add_child(group_name, child_group_name)
i = i + 1
# Add default group for this inventory if specified
self.group = to_safe_group_name(self.get_option('group'))
if self.group is not None and self.group != '':
self.group = to_safe_group_name(self.get_option("group"))
if self.group is not None and self.group != "":
self.inventory.add_group(self.group)
self.display.vvvv(f'Added site group {self.group}\n')
self.display.vvvv(f"Added site group {self.group}\n")
ip_addresses = {}
ipv6_addresses = {}
for host in self._get_systems():
# Get the FQDN for the host and add it to the right groups
if self.inventory_hostname == 'system':
hostname = make_unsafe(host['name']) # None
if self.inventory_hostname == "system":
hostname = make_unsafe(host["name"]) # None
else:
hostname = make_unsafe(host['hostname']) # None
interfaces = host['interfaces']
hostname = make_unsafe(host["hostname"]) # None
interfaces = host["interfaces"]
if set(host['mgmt_classes']) & set(self.include_mgmt_classes):
if set(host["mgmt_classes"]) & set(self.include_mgmt_classes):
self.display.vvvv(f"Including host {host['name']} in mgmt_classes {host['mgmt_classes']}\n")
else:
if self._exclude_profile(host['profile']):
if self._exclude_profile(host["profile"]):
self.display.vvvv(f"Excluding host {host['name']} in profile {host['profile']}\n")
continue
if set(host['mgmt_classes']) & set(self.exclude_mgmt_classes):
if set(host["mgmt_classes"]) & set(self.exclude_mgmt_classes):
self.display.vvvv(f"Excluding host {host['name']} in mgmt_classes {host['mgmt_classes']}\n")
continue
# hostname is often empty for non-static IP hosts
if hostname == '':
if hostname == "":
for iname, ivalue in interfaces.items():
if ivalue['management'] or not ivalue['static']:
this_dns_name = ivalue.get('dns_name', None)
if ivalue["management"] or not ivalue["static"]:
this_dns_name = ivalue.get("dns_name", None)
if this_dns_name is not None and this_dns_name != "":
hostname = make_unsafe(this_dns_name)
self.display.vvvv(f'Set hostname to {hostname} from {iname}\n')
self.display.vvvv(f"Set hostname to {hostname} from {iname}\n")
if hostname == '':
if hostname == "":
self.display.vvvv(f"Cannot determine hostname for host {host['name']}, skipping\n")
continue
@ -343,21 +348,21 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
self.display.vvvv(f"Added host {host['name']} hostname {hostname}\n")
# Add host to profile group
if host['profile'] != '':
group_name = self._add_safe_group_name(host['profile'], child=hostname)
self.display.vvvv(f'Added host {hostname} to profile group {group_name}\n')
if host["profile"] != "":
group_name = self._add_safe_group_name(host["profile"], child=hostname)
self.display.vvvv(f"Added host {hostname} to profile group {group_name}\n")
else:
self.display.warning(f'Host {hostname} has an empty profile\n')
self.display.warning(f"Host {hostname} has an empty profile\n")
# Add host to groups specified by group_by fields
for group_by in self.group_by:
if host[group_by] == '<<inherit>>' or host[group_by] == '':
if host[group_by] == "<<inherit>>" or host[group_by] == "":
groups = []
else:
groups = [host[group_by]] if isinstance(host[group_by], str) else host[group_by]
for group in groups:
group_name = self._add_safe_group_name(group, child=hostname)
self.display.vvvv(f'Added host {hostname} to group_by {group_by} group {group_name}\n')
self.display.vvvv(f"Added host {hostname} to group_by {group_by} group {group_name}\n")
# Add to group for this inventory
if self.group is not None:
@ -370,45 +375,45 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
ipv6_address_first = None
for iname, ivalue in interfaces.items():
# Set to first interface or management interface if defined or hostname matches dns_name
if ivalue['ip_address'] != "":
if ivalue["ip_address"] != "":
if ip_address_first is None:
ip_address_first = ivalue['ip_address']
if ivalue['management']:
ip_address = ivalue['ip_address']
elif ivalue['dns_name'] == hostname and ip_address is None:
ip_address = ivalue['ip_address']
if ivalue['ipv6_address'] != "":
ip_address_first = ivalue["ip_address"]
if ivalue["management"]:
ip_address = ivalue["ip_address"]
elif ivalue["dns_name"] == hostname and ip_address is None:
ip_address = ivalue["ip_address"]
if ivalue["ipv6_address"] != "":
if ipv6_address_first is None:
ipv6_address_first = ivalue['ipv6_address']
if ivalue['management']:
ipv6_address = ivalue['ipv6_address']
elif ivalue['dns_name'] == hostname and ipv6_address is None:
ipv6_address = ivalue['ipv6_address']
ipv6_address_first = ivalue["ipv6_address"]
if ivalue["management"]:
ipv6_address = ivalue["ipv6_address"]
elif ivalue["dns_name"] == hostname and ipv6_address is None:
ipv6_address = ivalue["ipv6_address"]
# Collect all interface name mappings for adding to group vars
if self.get_option('want_ip_addresses'):
if ivalue['dns_name'] != "":
if ivalue['ip_address'] != "":
ip_addresses[ivalue['dns_name']] = ivalue['ip_address']
if ivalue['ipv6_address'] != "":
ip_addresses[ivalue['dns_name']] = ivalue['ipv6_address']
if self.get_option("want_ip_addresses"):
if ivalue["dns_name"] != "":
if ivalue["ip_address"] != "":
ip_addresses[ivalue["dns_name"]] = ivalue["ip_address"]
if ivalue["ipv6_address"] != "":
ip_addresses[ivalue["dns_name"]] = ivalue["ipv6_address"]
# Add ip_address to host if defined, use first if no management or matched dns_name
if ip_address is None and ip_address_first is not None:
ip_address = ip_address_first
if ip_address is not None:
self.inventory.set_variable(hostname, 'cobbler_ipv4_address', make_unsafe(ip_address))
self.inventory.set_variable(hostname, "cobbler_ipv4_address", make_unsafe(ip_address))
if ipv6_address is None and ipv6_address_first is not None:
ipv6_address = ipv6_address_first
if ipv6_address is not None:
self.inventory.set_variable(hostname, 'cobbler_ipv6_address', make_unsafe(ipv6_address))
self.inventory.set_variable(hostname, "cobbler_ipv6_address", make_unsafe(ipv6_address))
if self.get_option('want_facts'):
if self.get_option("want_facts"):
try:
self.inventory.set_variable(hostname, 'cobbler', make_unsafe(host))
self.inventory.set_variable(hostname, "cobbler", make_unsafe(host))
except ValueError as e:
self.display.warning(f"Could not set host info for {hostname}: {e}")
if self.get_option('want_ip_addresses'):
self.inventory.set_variable(self.group, 'cobbler_ipv4_addresses', make_unsafe(ip_addresses))
self.inventory.set_variable(self.group, 'cobbler_ipv6_addresses', make_unsafe(ipv6_addresses))
if self.get_option("want_ip_addresses"):
self.inventory.set_variable(self.group, "cobbler_ipv4_addresses", make_unsafe(ip_addresses))
self.inventory.set_variable(self.group, "cobbler_ipv6_addresses", make_unsafe(ipv6_addresses))

View file

@ -87,53 +87,54 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
try:
import gitlab
HAS_GITLAB = True
except ImportError:
HAS_GITLAB = False
class InventoryModule(BaseInventoryPlugin, Constructable):
''' Host inventory parser for ansible using GitLab API as source. '''
"""Host inventory parser for ansible using GitLab API as source."""
NAME = 'community.general.gitlab_runners'
NAME = "community.general.gitlab_runners"
def _populate(self):
gl = gitlab.Gitlab(self.get_option('server_url'), private_token=self.get_option('api_token'))
self.inventory.add_group('gitlab_runners')
gl = gitlab.Gitlab(self.get_option("server_url"), private_token=self.get_option("api_token"))
self.inventory.add_group("gitlab_runners")
try:
if self.get_option('filter'):
runners = gl.runners.all(scope=self.get_option('filter'))
if self.get_option("filter"):
runners = gl.runners.all(scope=self.get_option("filter"))
else:
runners = gl.runners.all()
for runner in runners:
host = make_unsafe(str(runner['id']))
ip_address = runner['ip_address']
host_attrs = make_unsafe(vars(gl.runners.get(runner['id']))['_attrs'])
self.inventory.add_host(host, group='gitlab_runners')
self.inventory.set_variable(host, 'ansible_host', make_unsafe(ip_address))
if self.get_option('verbose_output', True):
self.inventory.set_variable(host, 'gitlab_runner_attributes', host_attrs)
host = make_unsafe(str(runner["id"]))
ip_address = runner["ip_address"]
host_attrs = make_unsafe(vars(gl.runners.get(runner["id"]))["_attrs"])
self.inventory.add_host(host, group="gitlab_runners")
self.inventory.set_variable(host, "ansible_host", make_unsafe(ip_address))
if self.get_option("verbose_output", True):
self.inventory.set_variable(host, "gitlab_runner_attributes", host_attrs)
# Use constructed if applicable
strict = self.get_option('strict')
strict = self.get_option("strict")
# Composed variables
self._set_composite_vars(self.get_option('compose'), host_attrs, host, strict=strict)
self._set_composite_vars(self.get_option("compose"), host_attrs, host, strict=strict)
# Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group
self._add_host_to_composed_groups(self.get_option('groups'), host_attrs, host, strict=strict)
self._add_host_to_composed_groups(self.get_option("groups"), host_attrs, host, strict=strict)
# Create groups based on variable values and add the corresponding hosts to it
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host_attrs, host, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), host_attrs, host, strict=strict)
except Exception as e:
raise AnsibleParserError(f'Unable to fetch hosts from GitLab API, this was the original exception: {e}')
raise AnsibleParserError(f"Unable to fetch hosts from GitLab API, this was the original exception: {e}")
def verify_file(self, path):
"""Return the possibly of a file being consumable by this plugin."""
return (
super().verify_file(path) and
path.endswith(("gitlab_runners.yaml", "gitlab_runners.yml")))
return super().verify_file(path) and path.endswith(("gitlab_runners.yaml", "gitlab_runners.yml"))
def parse(self, inventory, loader, path, cache=True):
if not HAS_GITLAB:
raise AnsibleError('The GitLab runners dynamic inventory plugin requires python-gitlab: https://python-gitlab.readthedocs.io/en/stable/')
raise AnsibleError(
"The GitLab runners dynamic inventory plugin requires python-gitlab: https://python-gitlab.readthedocs.io/en/stable/"
)
super().parse(inventory, loader, path, cache)
self._read_config_data(path)
self._populate()

View file

@ -104,12 +104,11 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
class InventoryModule(BaseInventoryPlugin, Constructable):
''' Host inventory parser for ansible using Icinga2 as source. '''
"""Host inventory parser for ansible using Icinga2 as source."""
NAME = 'community.general.icinga2'
NAME = "community.general.icinga2"
def __init__(self):
super().__init__()
# from config
@ -127,7 +126,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('icinga2.yaml', 'icinga2.yml')):
if path.endswith(("icinga2.yaml", "icinga2.yml")):
valid = True
else:
self.display.vvv('Skipping due to inventory source not ending in "icinga2.yaml" nor "icinga2.yml"')
@ -135,28 +134,28 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
def _api_connect(self):
self.headers = {
'User-Agent': "ansible-icinga2-inv",
'Accept': "application/json",
"User-Agent": "ansible-icinga2-inv",
"Accept": "application/json",
}
api_status_url = f"{self.icinga2_url}/status"
request_args = {
'headers': self.headers,
'url_username': self.icinga2_user,
'url_password': self.icinga2_password,
'validate_certs': self.ssl_verify
"headers": self.headers,
"url_username": self.icinga2_user,
"url_password": self.icinga2_password,
"validate_certs": self.ssl_verify,
}
open_url(api_status_url, **request_args)
def _post_request(self, request_url, data=None):
self.display.vvv(f"Requested URL: {request_url}")
request_args = {
'headers': self.headers,
'url_username': self.icinga2_user,
'url_password': self.icinga2_password,
'validate_certs': self.ssl_verify
"headers": self.headers,
"url_username": self.icinga2_user,
"url_password": self.icinga2_password,
"validate_certs": self.ssl_verify,
}
if data is not None:
request_args['data'] = json.dumps(data)
request_args["data"] = json.dumps(data)
self.display.vvv(f"Request Args: {request_args}")
try:
response = open_url(request_url, **request_args)
@ -166,51 +165,60 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
self.display.vvv(f"Error returned: {error_body}")
except Exception:
error_body = {"status": None}
if e.code == 404 and error_body.get('status') == "No objects found.":
if e.code == 404 and error_body.get("status") == "No objects found.":
raise AnsibleParserError("Host filter returned no data. Please confirm your host_filter value is valid")
raise AnsibleParserError(f"Unexpected data returned: {e} -- {error_body}")
response_body = response.read()
json_data = json.loads(response_body.decode('utf-8'))
json_data = json.loads(response_body.decode("utf-8"))
self.display.vvv(f"Returned Data: {json.dumps(json_data, indent=4, sort_keys=True)}")
if 200 <= response.status <= 299:
return json_data
if response.status == 404 and json_data['status'] == "No objects found.":
raise AnsibleParserError(
f"API returned no data -- Response: {response.status} - {json_data['status']}")
if response.status == 404 and json_data["status"] == "No objects found.":
raise AnsibleParserError(f"API returned no data -- Response: {response.status} - {json_data['status']}")
if response.status == 401:
raise AnsibleParserError(
f"API was unable to complete query -- Response: {response.status} - {json_data['status']}")
f"API was unable to complete query -- Response: {response.status} - {json_data['status']}"
)
if response.status == 500:
raise AnsibleParserError(
f"API Response - {json_data['status']} - {json_data['errors']}")
raise AnsibleParserError(
f"Unexpected data returned - {json_data['status']} - {json_data['errors']}")
raise AnsibleParserError(f"API Response - {json_data['status']} - {json_data['errors']}")
raise AnsibleParserError(f"Unexpected data returned - {json_data['status']} - {json_data['errors']}")
def _query_hosts(self, hosts=None, attrs=None, joins=None, host_filter=None):
query_hosts_url = f"{self.icinga2_url}/objects/hosts"
self.headers['X-HTTP-Method-Override'] = 'GET'
self.headers["X-HTTP-Method-Override"] = "GET"
data_dict = dict()
if hosts:
data_dict['hosts'] = hosts
data_dict["hosts"] = hosts
if attrs is not None:
data_dict['attrs'] = attrs
data_dict["attrs"] = attrs
if joins is not None:
data_dict['joins'] = joins
data_dict["joins"] = joins
if host_filter is not None:
data_dict['filter'] = host_filter.replace("\\\"", "\"")
data_dict["filter"] = host_filter.replace('\\"', '"')
self.display.vvv(host_filter)
host_dict = self._post_request(query_hosts_url, data_dict)
return host_dict['results']
return host_dict["results"]
def get_inventory_from_icinga(self):
"""Query for all hosts """
"""Query for all hosts"""
self.display.vvv("Querying Icinga2 for inventory")
query_args = {
"attrs": ["address", "address6", "name", "display_name", "state_type", "state", "templates", "groups", "vars", "zone"],
"attrs": [
"address",
"address6",
"name",
"display_name",
"state_type",
"state",
"templates",
"groups",
"vars",
"zone",
],
}
if self.host_filter is not None:
query_args['host_filter'] = self.host_filter
query_args["host_filter"] = self.host_filter
# Icinga2 API Call
results_json = self._query_hosts(**query_args)
# Manipulate returned API data to Ansible inventory spec
@ -218,10 +226,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
return ansible_inv
def _apply_constructable(self, name, variables):
strict = self.get_option('strict')
self._add_host_to_composed_groups(self.get_option('groups'), variables, name, strict=strict)
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), variables, name, strict=strict)
self._set_composite_vars(self.get_option('compose'), variables, name, strict=strict)
strict = self.get_option("strict")
self._add_host_to_composed_groups(self.get_option("groups"), variables, name, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), variables, name, strict=strict)
self._set_composite_vars(self.get_option("compose"), variables, name, strict=strict)
def _populate(self):
groups = self._to_json(self.get_inventory_from_icinga())
@ -235,58 +243,55 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
"""Convert Icinga2 API data to JSON format for Ansible"""
groups_dict = {"_meta": {"hostvars": {}}}
for entry in json_data:
host_attrs = make_unsafe(entry['attrs'])
host_attrs = make_unsafe(entry["attrs"])
if self.inventory_attr == "name":
host_name = make_unsafe(entry.get('name'))
host_name = make_unsafe(entry.get("name"))
if self.inventory_attr == "address":
# When looking for address for inventory, if missing fallback to object name
if host_attrs.get('address', '') != '':
host_name = make_unsafe(host_attrs.get('address'))
if host_attrs.get("address", "") != "":
host_name = make_unsafe(host_attrs.get("address"))
else:
host_name = make_unsafe(entry.get('name'))
host_name = make_unsafe(entry.get("name"))
if self.inventory_attr == "display_name":
host_name = host_attrs.get('display_name')
if host_attrs['state'] == 0:
host_attrs['state'] = 'on'
host_name = host_attrs.get("display_name")
if host_attrs["state"] == 0:
host_attrs["state"] = "on"
else:
host_attrs['state'] = 'off'
host_attrs["state"] = "off"
self.inventory.add_host(host_name)
if self.group_by_hostgroups:
host_groups = host_attrs.get('groups')
host_groups = host_attrs.get("groups")
for group in host_groups:
if group not in self.inventory.groups.keys():
self.inventory.add_group(group)
self.inventory.add_child(group, host_name)
# If the address attribute is populated, override ansible_host with the value
if host_attrs.get('address') != '':
self.inventory.set_variable(host_name, 'ansible_host', host_attrs.get('address'))
self.inventory.set_variable(host_name, 'hostname', make_unsafe(entry.get('name')))
self.inventory.set_variable(host_name, 'display_name', host_attrs.get('display_name'))
self.inventory.set_variable(host_name, 'state',
host_attrs['state'])
self.inventory.set_variable(host_name, 'state_type',
host_attrs['state_type'])
if host_attrs.get("address") != "":
self.inventory.set_variable(host_name, "ansible_host", host_attrs.get("address"))
self.inventory.set_variable(host_name, "hostname", make_unsafe(entry.get("name")))
self.inventory.set_variable(host_name, "display_name", host_attrs.get("display_name"))
self.inventory.set_variable(host_name, "state", host_attrs["state"])
self.inventory.set_variable(host_name, "state_type", host_attrs["state_type"])
# Adds all attributes to a variable 'icinga2_attributes'
construct_vars = dict(self.inventory.get_host(host_name).get_vars())
construct_vars['icinga2_attributes'] = host_attrs
construct_vars["icinga2_attributes"] = host_attrs
self._apply_constructable(host_name, construct_vars)
return groups_dict
def parse(self, inventory, loader, path, cache=True):
super().parse(inventory, loader, path)
# read config from file, this sets 'options'
self._read_config_data(path)
# Store the options from the YAML file
self.icinga2_url = self.get_option('url')
self.icinga2_user = self.get_option('user')
self.icinga2_password = self.get_option('password')
self.ssl_verify = self.get_option('validate_certs')
self.host_filter = self.get_option('host_filter')
self.inventory_attr = self.get_option('inventory_attr')
self.group_by_hostgroups = self.get_option('group_by_hostgroups')
self.icinga2_url = self.get_option("url")
self.icinga2_user = self.get_option("user")
self.icinga2_password = self.get_option("password")
self.ssl_verify = self.get_option("validate_certs")
self.host_filter = self.get_option("host_filter")
self.inventory_attr = self.get_option("inventory_attr")
self.group_by_hostgroups = self.get_option("group_by_hostgroups")
if self.templar.is_template(self.icinga2_url):
self.icinga2_url = self.templar.template(variable=self.icinga2_url)

View file

@ -106,9 +106,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
if path.endswith(("incus.yaml", "incus.yml")):
valid = True
else:
self.display.vvv(
'Skipping due to inventory source not ending in "incus.yaml" nor "incus.yml"'
)
self.display.vvv('Skipping due to inventory source not ending in "incus.yaml" nor "incus.yml"')
return valid
@ -148,10 +146,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
if project_name:
projects = [project_name]
else:
projects = [
entry["name"]
for entry in self._run_incus("project", "list", f"{remote_name}:")
]
projects = [entry["name"] for entry in self._run_incus("project", "list", f"{remote_name}:")]
# Get a list of instances.
for project in projects:
@ -214,17 +209,11 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
strict = self.get_option("strict")
# Add variables created by the user's Jinja2 expressions to the host
self._set_composite_vars(
self.get_option("compose"), host_vars, hostname, strict=True
)
self._set_composite_vars(self.get_option("compose"), host_vars, hostname, strict=True)
# Create user-defined groups using variables and Jinja2 conditionals
self._add_host_to_composed_groups(
self.get_option("groups"), host_vars, hostname, strict=strict
)
self._add_host_to_keyed_groups(
self.get_option("keyed_groups"), host_vars, hostname, strict=strict
)
self._add_host_to_composed_groups(self.get_option("groups"), host_vars, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), host_vars, hostname, strict=strict)
def _run_incus(self, *args):
local_cmd = ["incus"] + list(args) + ["--format=json"]

View file

@ -1,4 +1,3 @@
# Copyright (c) 2024 Vladimir Botka <vbotka@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
@ -184,34 +183,34 @@ display = Display()
def _parse_ip4(ip4):
''' Return dictionary iocage_ip4_dict. default = {ip4: [], msg: ''}.
If item matches ifc|IP or ifc|CIDR parse ifc, ip, and mask.
Otherwise, append item to msg.
'''
"""Return dictionary iocage_ip4_dict. default = {ip4: [], msg: ''}.
If item matches ifc|IP or ifc|CIDR parse ifc, ip, and mask.
Otherwise, append item to msg.
"""
iocage_ip4_dict = {}
iocage_ip4_dict['ip4'] = []
iocage_ip4_dict['msg'] = ''
iocage_ip4_dict["ip4"] = []
iocage_ip4_dict["msg"] = ""
items = ip4.split(',')
items = ip4.split(",")
for item in items:
if re.match('^\\w+\\|(?:\\d{1,3}\\.){3}\\d{1,3}.*$', item):
i = re.split('\\||/', item)
if re.match("^\\w+\\|(?:\\d{1,3}\\.){3}\\d{1,3}.*$", item):
i = re.split("\\||/", item)
if len(i) == 3:
iocage_ip4_dict['ip4'].append({'ifc': i[0], 'ip': i[1], 'mask': i[2]})
iocage_ip4_dict["ip4"].append({"ifc": i[0], "ip": i[1], "mask": i[2]})
else:
iocage_ip4_dict['ip4'].append({'ifc': i[0], 'ip': i[1], 'mask': '-'})
iocage_ip4_dict["ip4"].append({"ifc": i[0], "ip": i[1], "mask": "-"})
else:
iocage_ip4_dict['msg'] += item
iocage_ip4_dict["msg"] += item
return iocage_ip4_dict
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
''' Host inventory parser for ansible using iocage as source. '''
"""Host inventory parser for ansible using iocage as source."""
NAME = 'community.general.iocage'
IOCAGE = '/usr/local/bin/iocage'
NAME = "community.general.iocage"
IOCAGE = "/usr/local/bin/iocage"
def __init__(self):
super().__init__()
@ -219,7 +218,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('iocage.yaml', 'iocage.yml')):
if path.endswith(("iocage.yaml", "iocage.yml")):
valid = True
else:
self.display.vvv('Skipping due to inventory source not ending in "iocage.yaml" nor "iocage.yml"')
@ -231,7 +230,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path)
cache_key = self.get_cache_key(path)
user_cache_setting = self.get_option('cache')
user_cache_setting = self.get_option("cache")
attempt_to_read_cache = user_cache_setting and cache
cache_needs_update = user_cache_setting and not cache
@ -248,52 +247,52 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self.populate(results)
def get_inventory(self, path):
host = self.get_option('host')
sudo = self.get_option('sudo')
sudo_preserve_env = self.get_option('sudo_preserve_env')
env = self.get_option('env')
get_properties = self.get_option('get_properties')
hooks_results = self.get_option('hooks_results')
inventory_hostname_tag = self.get_option('inventory_hostname_tag')
inventory_hostname_required = self.get_option('inventory_hostname_required')
host = self.get_option("host")
sudo = self.get_option("sudo")
sudo_preserve_env = self.get_option("sudo_preserve_env")
env = self.get_option("env")
get_properties = self.get_option("get_properties")
hooks_results = self.get_option("hooks_results")
inventory_hostname_tag = self.get_option("inventory_hostname_tag")
inventory_hostname_required = self.get_option("inventory_hostname_required")
cmd = []
my_env = os.environ.copy()
if host == 'localhost':
if host == "localhost":
my_env.update({str(k): str(v) for k, v in env.items()})
else:
user = self.get_option('user')
user = self.get_option("user")
cmd.append("ssh")
cmd.append(f"{user}@{host}")
cmd.extend([f"{k}={v}" for k, v in env.items()])
cmd_list = cmd.copy()
if sudo:
cmd_list.append('sudo')
cmd_list.append("sudo")
if sudo_preserve_env:
cmd_list.append('--preserve-env')
cmd_list.append("--preserve-env")
cmd_list.append(self.IOCAGE)
cmd_list.append('list')
cmd_list.append('--long')
cmd_list.append("list")
cmd_list.append("--long")
try:
p = Popen(cmd_list, stdout=PIPE, stderr=PIPE, env=my_env)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(f'Failed to run cmd={cmd_list}, rc={p.returncode}, stderr={to_native(stderr)}')
raise AnsibleError(f"Failed to run cmd={cmd_list}, rc={p.returncode}, stderr={to_native(stderr)}")
try:
t_stdout = to_text(stdout, errors='surrogate_or_strict')
t_stdout = to_text(stdout, errors="surrogate_or_strict")
except UnicodeError as e:
raise AnsibleError(f'Invalid (non unicode) input returned: {e}') from e
raise AnsibleError(f"Invalid (non unicode) input returned: {e}") from e
except Exception as e:
raise AnsibleParserError(f'Failed to parse {to_native(path)}: {e}') from e
raise AnsibleParserError(f"Failed to parse {to_native(path)}: {e}") from e
results = {'_meta': {'hostvars': {}}}
results = {"_meta": {"hostvars": {}}}
self.get_jails(t_stdout, results)
if get_properties:
for hostname, host_vars in results['_meta']['hostvars'].items():
for hostname, host_vars in results["_meta"]["hostvars"].items():
cmd_get_properties = cmd.copy()
cmd_get_properties.append(self.IOCAGE)
cmd_get_properties.append("get")
@ -304,76 +303,78 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(
f'Failed to run cmd={cmd_get_properties}, rc={p.returncode}, stderr={to_native(stderr)}')
f"Failed to run cmd={cmd_get_properties}, rc={p.returncode}, stderr={to_native(stderr)}"
)
try:
t_stdout = to_text(stdout, errors='surrogate_or_strict')
t_stdout = to_text(stdout, errors="surrogate_or_strict")
except UnicodeError as e:
raise AnsibleError(f'Invalid (non unicode) input returned: {e}') from e
raise AnsibleError(f"Invalid (non unicode) input returned: {e}") from e
except Exception as e:
raise AnsibleError(f'Failed to get properties: {e}') from e
raise AnsibleError(f"Failed to get properties: {e}") from e
self.get_properties(t_stdout, results, hostname)
if hooks_results:
cmd_get_pool = cmd.copy()
cmd_get_pool.append(self.IOCAGE)
cmd_get_pool.append('get')
cmd_get_pool.append('--pool')
cmd_get_pool.append("get")
cmd_get_pool.append("--pool")
try:
p = Popen(cmd_get_pool, stdout=PIPE, stderr=PIPE, env=my_env)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleError(
f'Failed to run cmd={cmd_get_pool}, rc={p.returncode}, stderr={to_native(stderr)}')
f"Failed to run cmd={cmd_get_pool}, rc={p.returncode}, stderr={to_native(stderr)}"
)
try:
iocage_pool = to_text(stdout, errors='surrogate_or_strict').strip()
iocage_pool = to_text(stdout, errors="surrogate_or_strict").strip()
except UnicodeError as e:
raise AnsibleError(f'Invalid (non unicode) input returned: {e}') from e
raise AnsibleError(f"Invalid (non unicode) input returned: {e}") from e
except Exception as e:
raise AnsibleError(f'Failed to get pool: {e}') from e
raise AnsibleError(f"Failed to get pool: {e}") from e
for hostname, host_vars in results['_meta']['hostvars'].items():
for hostname, host_vars in results["_meta"]["hostvars"].items():
iocage_hooks = []
for hook in hooks_results:
path = f"/{iocage_pool}/iocage/jails/{hostname}/root{hook}"
cmd_cat_hook = cmd.copy()
cmd_cat_hook.append('cat')
cmd_cat_hook.append("cat")
cmd_cat_hook.append(path)
try:
p = Popen(cmd_cat_hook, stdout=PIPE, stderr=PIPE, env=my_env)
stdout, stderr = p.communicate()
if p.returncode != 0:
iocage_hooks.append('-')
iocage_hooks.append("-")
continue
try:
iocage_hook = to_text(stdout, errors='surrogate_or_strict').strip()
iocage_hook = to_text(stdout, errors="surrogate_or_strict").strip()
except UnicodeError as e:
raise AnsibleError(f'Invalid (non unicode) input returned: {e}') from e
raise AnsibleError(f"Invalid (non unicode) input returned: {e}") from e
except Exception:
iocage_hooks.append('-')
iocage_hooks.append("-")
else:
iocage_hooks.append(iocage_hook)
results['_meta']['hostvars'][hostname]['iocage_hooks'] = iocage_hooks
results["_meta"]["hostvars"][hostname]["iocage_hooks"] = iocage_hooks
# Optionally, get the jails names from the properties notes.
# Requires the notes format "t1=v1 t2=v2 ..."
if inventory_hostname_tag:
if not get_properties:
raise AnsibleError('Jail properties are needed to use inventory_hostname_tag. Enable get_properties')
raise AnsibleError("Jail properties are needed to use inventory_hostname_tag. Enable get_properties")
update = {}
for hostname, host_vars in results['_meta']['hostvars'].items():
tags = dict(tag.split('=', 1) for tag in host_vars['iocage_properties']['notes'].split() if '=' in tag)
for hostname, host_vars in results["_meta"]["hostvars"].items():
tags = dict(tag.split("=", 1) for tag in host_vars["iocage_properties"]["notes"].split() if "=" in tag)
if inventory_hostname_tag in tags:
update[hostname] = tags[inventory_hostname_tag]
elif inventory_hostname_required:
raise AnsibleError(f'Mandatory tag {inventory_hostname_tag!r} is missing in the properties notes.')
raise AnsibleError(f"Mandatory tag {inventory_hostname_tag!r} is missing in the properties notes.")
for hostname, alias in update.items():
results['_meta']['hostvars'][alias] = results['_meta']['hostvars'].pop(hostname)
results["_meta"]["hostvars"][alias] = results["_meta"]["hostvars"].pop(hostname)
return results
@ -381,38 +382,38 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
lines = t_stdout.splitlines()
if len(lines) < 5:
return
indices = [i for i, val in enumerate(lines[1]) if val == '|']
indices = [i for i, val in enumerate(lines[1]) if val == "|"]
for line in lines[3::2]:
jail = [line[i + 1:j].strip() for i, j in zip(indices[:-1], indices[1:])]
jail = [line[i + 1 : j].strip() for i, j in zip(indices[:-1], indices[1:])]
iocage_name = jail[1]
iocage_ip4_dict = _parse_ip4(jail[6])
if iocage_ip4_dict['ip4']:
iocage_ip4 = ','.join([d['ip'] for d in iocage_ip4_dict['ip4']])
if iocage_ip4_dict["ip4"]:
iocage_ip4 = ",".join([d["ip"] for d in iocage_ip4_dict["ip4"]])
else:
iocage_ip4 = '-'
results['_meta']['hostvars'][iocage_name] = {}
results['_meta']['hostvars'][iocage_name]['iocage_jid'] = jail[0]
results['_meta']['hostvars'][iocage_name]['iocage_boot'] = jail[2]
results['_meta']['hostvars'][iocage_name]['iocage_state'] = jail[3]
results['_meta']['hostvars'][iocage_name]['iocage_type'] = jail[4]
results['_meta']['hostvars'][iocage_name]['iocage_release'] = jail[5]
results['_meta']['hostvars'][iocage_name]['iocage_ip4_dict'] = iocage_ip4_dict
results['_meta']['hostvars'][iocage_name]['iocage_ip4'] = iocage_ip4
results['_meta']['hostvars'][iocage_name]['iocage_ip6'] = jail[7]
results['_meta']['hostvars'][iocage_name]['iocage_template'] = jail[8]
results['_meta']['hostvars'][iocage_name]['iocage_basejail'] = jail[9]
iocage_ip4 = "-"
results["_meta"]["hostvars"][iocage_name] = {}
results["_meta"]["hostvars"][iocage_name]["iocage_jid"] = jail[0]
results["_meta"]["hostvars"][iocage_name]["iocage_boot"] = jail[2]
results["_meta"]["hostvars"][iocage_name]["iocage_state"] = jail[3]
results["_meta"]["hostvars"][iocage_name]["iocage_type"] = jail[4]
results["_meta"]["hostvars"][iocage_name]["iocage_release"] = jail[5]
results["_meta"]["hostvars"][iocage_name]["iocage_ip4_dict"] = iocage_ip4_dict
results["_meta"]["hostvars"][iocage_name]["iocage_ip4"] = iocage_ip4
results["_meta"]["hostvars"][iocage_name]["iocage_ip6"] = jail[7]
results["_meta"]["hostvars"][iocage_name]["iocage_template"] = jail[8]
results["_meta"]["hostvars"][iocage_name]["iocage_basejail"] = jail[9]
def get_properties(self, t_stdout, results, hostname):
properties = dict(x.split(':', 1) for x in t_stdout.splitlines())
results['_meta']['hostvars'][hostname]['iocage_properties'] = properties
properties = dict(x.split(":", 1) for x in t_stdout.splitlines())
results["_meta"]["hostvars"][hostname]["iocage_properties"] = properties
def populate(self, results):
strict = self.get_option('strict')
strict = self.get_option("strict")
for hostname, host_vars in results['_meta']['hostvars'].items():
self.inventory.add_host(hostname, group='all')
for hostname, host_vars in results["_meta"]["hostvars"].items():
self.inventory.add_host(hostname, group="all")
for var, value in host_vars.items():
self.inventory.set_variable(hostname, var, value)
self._set_composite_vars(self.get_option('compose'), host_vars, hostname, strict=True)
self._add_host_to_composed_groups(self.get_option('groups'), host_vars, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host_vars, hostname, strict=strict)
self._set_composite_vars(self.get_option("compose"), host_vars, hostname, strict=True)
self._add_host_to_composed_groups(self.get_option("groups"), host_vars, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), host_vars, hostname, strict=strict)

View file

@ -134,27 +134,24 @@ try:
from linode_api4 import LinodeClient
from linode_api4.objects.linode import Instance
from linode_api4.errors import ApiError as LinodeApiError
HAS_LINODE = True
except ImportError:
HAS_LINODE = False
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
NAME = 'community.general.linode'
NAME = "community.general.linode"
def _build_client(self, loader):
"""Build the Linode client."""
access_token = self.get_option('access_token')
access_token = self.get_option("access_token")
if self.templar.is_template(access_token):
access_token = self.templar.template(variable=access_token)
if access_token is None:
raise AnsibleError((
'Could not retrieve Linode access token '
'from plugin configuration sources'
))
raise AnsibleError(("Could not retrieve Linode access token from plugin configuration sources"))
self.client = LinodeClient(access_token)
@ -163,7 +160,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
try:
self.instances = self.client.linode.instances()
except LinodeApiError as exception:
raise AnsibleError(f'Linode client raised: {exception}')
raise AnsibleError(f"Linode client raised: {exception}")
def _add_groups(self):
"""Add Linode instance groups to the dynamic inventory."""
@ -174,26 +171,17 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def _filter_by_config(self):
"""Filter instances by user specified configuration."""
regions = self.get_option('regions')
regions = self.get_option("regions")
if regions:
self.instances = [
instance for instance in self.instances
if instance.region.id in regions
]
self.instances = [instance for instance in self.instances if instance.region.id in regions]
types = self.get_option('types')
types = self.get_option("types")
if types:
self.instances = [
instance for instance in self.instances
if instance.type.id in types
]
self.instances = [instance for instance in self.instances if instance.type.id in types]
tags = self.get_option('tags')
tags = self.get_option("tags")
if tags:
self.instances = [
instance for instance in self.instances
if any(tag in instance.tags for tag in tags)
]
self.instances = [instance for instance in self.instances if any(tag in instance.tags for tag in tags)]
def _add_instances_to_groups(self):
"""Add instance names to their dynamic inventory groups."""
@ -202,28 +190,22 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def _add_hostvars_for_instances(self):
"""Add hostvars for instances in the dynamic inventory."""
ip_style = self.get_option('ip_style')
ip_style = self.get_option("ip_style")
for instance in self.instances:
hostvars = instance._raw_json
hostname = make_unsafe(instance.label)
for hostvar_key in hostvars:
if ip_style == 'api' and hostvar_key in ['ipv4', 'ipv6']:
if ip_style == "api" and hostvar_key in ["ipv4", "ipv6"]:
continue
self.inventory.set_variable(
hostname,
hostvar_key,
make_unsafe(hostvars[hostvar_key])
)
if ip_style == 'api':
self.inventory.set_variable(hostname, hostvar_key, make_unsafe(hostvars[hostvar_key]))
if ip_style == "api":
ips = instance.ips.ipv4.public + instance.ips.ipv4.private
ips += [instance.ips.ipv6.slaac, instance.ips.ipv6.link_local]
ips += instance.ips.ipv6.pools
for ip_type in set(ip.type for ip in ips):
self.inventory.set_variable(
hostname,
ip_type,
make_unsafe(self._ip_data([ip for ip in ips if ip.type == ip_type]))
hostname, ip_type, make_unsafe(self._ip_data([ip for ip in ips if ip.type == ip_type]))
)
def _ip_data(self, ip_list):
@ -231,13 +213,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
for ip in list(ip_list):
data.append(
{
'address': ip.address,
'subnet_mask': ip.subnet_mask,
'gateway': ip.gateway,
'public': ip.public,
'prefix': ip.prefix,
'rdns': ip.rdns,
'type': ip.type
"address": ip.address,
"subnet_mask": ip.subnet_mask,
"gateway": ip.gateway,
"public": ip.public,
"prefix": ip.prefix,
"rdns": ip.rdns,
"type": ip.type,
}
)
return data
@ -246,7 +228,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return [i._raw_json for i in self.instances]
def populate(self):
strict = self.get_option('strict')
strict = self.get_option("strict")
self._filter_by_config()
@ -256,21 +238,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
for instance in self.instances:
hostname = make_unsafe(instance.label)
variables = self.inventory.get_host(hostname).get_vars()
self._add_host_to_composed_groups(
self.get_option('groups'),
variables,
hostname,
strict=strict)
self._add_host_to_keyed_groups(
self.get_option('keyed_groups'),
variables,
hostname,
strict=strict)
self._set_composite_vars(
self.get_option('compose'),
variables,
hostname,
strict=strict)
self._add_host_to_composed_groups(self.get_option("groups"), variables, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), variables, hostname, strict=strict)
self._set_composite_vars(self.get_option("compose"), variables, hostname, strict=strict)
def verify_file(self, path):
"""Verify the Linode configuration file.
@ -299,14 +269,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self.instances = None
if not HAS_LINODE:
raise AnsibleError('the Linode dynamic inventory plugin requires linode_api4.')
raise AnsibleError("the Linode dynamic inventory plugin requires linode_api4.")
self._read_config_data(path)
cache_key = self.get_cache_key(path)
if cache:
cache = self.get_option('cache')
cache = self.get_option("cache")
update_cache = False
if cache:

View file

@ -190,9 +190,9 @@ else:
class InventoryModule(BaseInventoryPlugin):
DEBUG = 4
NAME = 'community.general.lxd'
SNAP_SOCKET_URL = 'unix:/var/snap/lxd/common/lxd/unix.socket'
SOCKET_URL = 'unix:/var/lib/lxd/unix.socket'
NAME = "community.general.lxd"
SNAP_SOCKET_URL = "unix:/var/snap/lxd/common/lxd/unix.socket"
SOCKET_URL = "unix:/var/lib/lxd/unix.socket"
@staticmethod
def load_json_data(path):
@ -210,10 +210,10 @@ class InventoryModule(BaseInventoryPlugin):
Returns:
dict(json_data): json data"""
try:
with open(path, 'r') as json_file:
with open(path, "r") as json_file:
return json.load(json_file)
except (IOError, json.decoder.JSONDecodeError) as err:
raise AnsibleParserError(f'Could not load the test data from {to_native(path)}: {err}')
raise AnsibleParserError(f"Could not load the test data from {to_native(path)}: {err}")
def save_json_data(self, path, file_name=None):
"""save data as json
@ -233,17 +233,17 @@ class InventoryModule(BaseInventoryPlugin):
if file_name:
path.append(file_name)
else:
prefix = 'lxd_data-'
time_stamp = time.strftime('%Y%m%d-%H%M%S')
suffix = '.atd'
prefix = "lxd_data-"
time_stamp = time.strftime("%Y%m%d-%H%M%S")
suffix = ".atd"
path.append(prefix + time_stamp + suffix)
try:
cwd = os.path.abspath(os.path.dirname(__file__))
with open(os.path.abspath(os.path.join(cwd, *path)), 'w') as json_file:
with open(os.path.abspath(os.path.join(cwd, *path)), "w") as json_file:
json.dump(self.data, json_file)
except IOError as err:
raise AnsibleParserError(f'Could not save data: {err}')
raise AnsibleParserError(f"Could not save data: {err}")
def verify_file(self, path):
"""Check the config
@ -260,7 +260,7 @@ class InventoryModule(BaseInventoryPlugin):
bool(valid): is valid"""
valid = False
if super().verify_file(path):
if path.endswith(('lxd.yaml', 'lxd.yml')):
if path.endswith(("lxd.yaml", "lxd.yml")):
valid = True
else:
self.display.vvv('Inventory source not ending in "lxd.yaml" or "lxd.yml"')
@ -282,8 +282,8 @@ class InventoryModule(BaseInventoryPlugin):
bool"""
if not isinstance(url, str):
return False
if not url.startswith(('unix:', 'https:')):
raise AnsibleError(f'URL is malformed: {url}')
if not url.startswith(("unix:", "https:")):
raise AnsibleError(f"URL is malformed: {url}")
return True
def _connect_to_socket(self):
@ -300,15 +300,17 @@ class InventoryModule(BaseInventoryPlugin):
Returns:
None"""
error_storage = {}
url_list = [self.get_option('url'), self.SNAP_SOCKET_URL, self.SOCKET_URL]
url_list = [self.get_option("url"), self.SNAP_SOCKET_URL, self.SOCKET_URL]
urls = (url for url in url_list if self.validate_url(url))
for url in urls:
try:
socket_connection = LXDClient(url, self.client_key, self.client_cert, self.debug, self.server_cert, self.server_check_hostname)
socket_connection = LXDClient(
url, self.client_key, self.client_cert, self.debug, self.server_cert, self.server_check_hostname
)
return socket_connection
except LXDClientException as err:
error_storage[url] = err
raise AnsibleError(f'No connection to the socket: {error_storage}')
raise AnsibleError(f"No connection to the socket: {error_storage}")
def _get_networks(self):
"""Get Networknames
@ -330,8 +332,8 @@ class InventoryModule(BaseInventoryPlugin):
# 'error_code': 0,
# 'error': '',
# 'metadata': ['/1.0/networks/lxdbr0']}
network_configs = self.socket.do('GET', '/1.0/networks')
return [m.split('/')[3] for m in network_configs['metadata']]
network_configs = self.socket.do("GET", "/1.0/networks")
return [m.split("/")[3] for m in network_configs["metadata"]]
def _get_instances(self):
"""Get instancenames
@ -355,16 +357,16 @@ class InventoryModule(BaseInventoryPlugin):
# "status_code": 200,
# "type": "sync"
# }
url = '/1.0/instances'
url = "/1.0/instances"
if self.project:
url = f"{url}?{urlencode(dict(project=self.project))}"
instances = self.socket.do('GET', url)
instances = self.socket.do("GET", url)
if self.project:
return [m.split('/')[3].split('?')[0] for m in instances['metadata']]
return [m.split("/")[3].split("?")[0] for m in instances["metadata"]]
return [m.split('/')[3] for m in instances['metadata']]
return [m.split("/")[3] for m in instances["metadata"]]
def _get_config(self, branch, name):
"""Get inventory of instance
@ -384,11 +386,18 @@ class InventoryModule(BaseInventoryPlugin):
dict(config): Config of the instance"""
config = {}
if isinstance(branch, (tuple, list)):
config[name] = {branch[1]: self.socket.do(
'GET', f'/1.0/{to_native(branch[0])}/{to_native(name)}/{to_native(branch[1])}?{urlencode(dict(project=self.project))}')}
config[name] = {
branch[1]: self.socket.do(
"GET",
f"/1.0/{to_native(branch[0])}/{to_native(name)}/{to_native(branch[1])}?{urlencode(dict(project=self.project))}",
)
}
else:
config[name] = {branch: self.socket.do(
'GET', f'/1.0/{to_native(branch)}/{to_native(name)}?{urlencode(dict(project=self.project))}')}
config[name] = {
branch: self.socket.do(
"GET", f"/1.0/{to_native(branch)}/{to_native(name)}?{urlencode(dict(project=self.project))}"
)
}
return config
def get_instance_data(self, names):
@ -406,11 +415,11 @@ class InventoryModule(BaseInventoryPlugin):
None"""
# tuple(('instances','metadata/templates')) to get section in branch
# e.g. /1.0/instances/<name>/metadata/templates
branches = ['instances', ('instances', 'state')]
branches = ["instances", ("instances", "state")]
instance_config = {}
for branch in branches:
for name in names:
instance_config['instances'] = self._get_config(branch, name)
instance_config["instances"] = self._get_config(branch, name)
self.data = dict_merge(instance_config, self.data)
def get_network_data(self, names):
@ -428,14 +437,14 @@ class InventoryModule(BaseInventoryPlugin):
None"""
# tuple(('instances','metadata/templates')) to get section in branch
# e.g. /1.0/instances/<name>/metadata/templates
branches = [('networks', 'state')]
branches = [("networks", "state")]
network_config = {}
for branch in branches:
for name in names:
try:
network_config['networks'] = self._get_config(branch, name)
network_config["networks"] = self._get_config(branch, name)
except LXDClientException:
network_config['networks'] = {name: None}
network_config["networks"] = {name: None}
self.data = dict_merge(network_config, self.data)
def extract_network_information_from_instance_config(self, instance_name):
@ -451,20 +460,26 @@ class InventoryModule(BaseInventoryPlugin):
None
Returns:
dict(network_configuration): network config"""
instance_network_interfaces = self._get_data_entry(f'instances/{instance_name}/state/metadata/network')
instance_network_interfaces = self._get_data_entry(f"instances/{instance_name}/state/metadata/network")
network_configuration = None
if instance_network_interfaces:
network_configuration = {}
gen_interface_names = [interface_name for interface_name in instance_network_interfaces if interface_name != 'lo']
gen_interface_names = [
interface_name for interface_name in instance_network_interfaces if interface_name != "lo"
]
for interface_name in gen_interface_names:
gen_address = [address for address in instance_network_interfaces[interface_name]['addresses'] if address.get('scope') != 'link']
gen_address = [
address
for address in instance_network_interfaces[interface_name]["addresses"]
if address.get("scope") != "link"
]
network_configuration[interface_name] = []
for address in gen_address:
address_set = {}
address_set['family'] = address.get('family')
address_set['address'] = address.get('address')
address_set['netmask'] = address.get('netmask')
address_set['combined'] = f"{address.get('address')}/{address.get('netmask')}"
address_set["family"] = address.get("family")
address_set["address"] = address.get("address")
address_set["netmask"] = address.get("netmask")
address_set["combined"] = f"{address.get('address')}/{address.get('netmask')}"
network_configuration[interface_name].append(address_set)
return network_configuration
@ -481,11 +496,15 @@ class InventoryModule(BaseInventoryPlugin):
None
Returns:
str(prefered_interface): None or interface name"""
instance_network_interfaces = self._get_data_entry(f'inventory/{instance_name}/network_interfaces')
instance_network_interfaces = self._get_data_entry(f"inventory/{instance_name}/network_interfaces")
prefered_interface = None # init
if instance_network_interfaces: # instance have network interfaces
# generator if interfaces which start with the desired pattern
net_generator = [interface for interface in instance_network_interfaces if interface.startswith(self.prefered_instance_network_interface)]
net_generator = [
interface
for interface in instance_network_interfaces
if interface.startswith(self.prefered_instance_network_interface)
]
selected_interfaces = [] # init
for interface in net_generator:
selected_interfaces.append(interface)
@ -508,9 +527,11 @@ class InventoryModule(BaseInventoryPlugin):
None"""
# get network device configuration and store {network: vlan_id}
network_vlans = {}
for network in self._get_data_entry('networks'):
if self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network)):
network_vlans[network] = self._get_data_entry('state/metadata/vlan/vid', data=self.data['networks'].get(network))
for network in self._get_data_entry("networks"):
if self._get_data_entry("state/metadata/vlan/vid", data=self.data["networks"].get(network)):
network_vlans[network] = self._get_data_entry(
"state/metadata/vlan/vid", data=self.data["networks"].get(network)
)
# get networkdevices of instance and return
# e.g.
@ -518,14 +539,14 @@ class InventoryModule(BaseInventoryPlugin):
# "network":"lxdbr0",
# "type":"nic"},
vlan_ids = {}
devices = self._get_data_entry(f'instances/{to_native(instance_name)}/instances/metadata/expanded_devices')
devices = self._get_data_entry(f"instances/{to_native(instance_name)}/instances/metadata/expanded_devices")
for device in devices:
if 'network' in devices[device]:
if devices[device]['network'] in network_vlans:
vlan_ids[devices[device].get('network')] = network_vlans[devices[device].get('network')]
if "network" in devices[device]:
if devices[device]["network"] in network_vlans:
vlan_ids[devices[device].get("network")] = network_vlans[devices[device].get("network")]
return vlan_ids if vlan_ids else None
def _get_data_entry(self, path, data=None, delimiter='/'):
def _get_data_entry(self, path, data=None, delimiter="/"):
"""Helper to get data
Helper to get data from self.data by a path like 'path/to/target'
@ -571,7 +592,7 @@ class InventoryModule(BaseInventoryPlugin):
Returns:
None"""
if not path:
path = self.data['inventory']
path = self.data["inventory"]
if instance_name not in path:
path[instance_name] = {}
@ -597,29 +618,53 @@ class InventoryModule(BaseInventoryPlugin):
Returns:
None"""
# create branch "inventory"
if 'inventory' not in self.data:
self.data['inventory'] = {}
if "inventory" not in self.data:
self.data["inventory"] = {}
for instance_name in self.data['instances']:
self._set_data_entry(instance_name, 'os', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/config/image.os'))
self._set_data_entry(instance_name, 'release', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/config/image.release'))
self._set_data_entry(instance_name, 'version', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/config/image.version'))
self._set_data_entry(instance_name, 'profile', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/profiles'))
self._set_data_entry(instance_name, 'location', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/location'))
self._set_data_entry(instance_name, 'state', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/config/volatile.last_state.power'))
self._set_data_entry(instance_name, 'type', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/type'))
self._set_data_entry(instance_name, 'network_interfaces', self.extract_network_information_from_instance_config(instance_name))
self._set_data_entry(instance_name, 'preferred_interface', self.get_prefered_instance_network_interface(instance_name))
self._set_data_entry(instance_name, 'vlan_ids', self.get_instance_vlans(instance_name))
self._set_data_entry(instance_name, 'project', self._get_data_entry(
f'instances/{instance_name}/instances/metadata/project'))
for instance_name in self.data["instances"]:
self._set_data_entry(
instance_name,
"os",
self._get_data_entry(f"instances/{instance_name}/instances/metadata/config/image.os"),
)
self._set_data_entry(
instance_name,
"release",
self._get_data_entry(f"instances/{instance_name}/instances/metadata/config/image.release"),
)
self._set_data_entry(
instance_name,
"version",
self._get_data_entry(f"instances/{instance_name}/instances/metadata/config/image.version"),
)
self._set_data_entry(
instance_name, "profile", self._get_data_entry(f"instances/{instance_name}/instances/metadata/profiles")
)
self._set_data_entry(
instance_name,
"location",
self._get_data_entry(f"instances/{instance_name}/instances/metadata/location"),
)
self._set_data_entry(
instance_name,
"state",
self._get_data_entry(f"instances/{instance_name}/instances/metadata/config/volatile.last_state.power"),
)
self._set_data_entry(
instance_name, "type", self._get_data_entry(f"instances/{instance_name}/instances/metadata/type")
)
self._set_data_entry(
instance_name,
"network_interfaces",
self.extract_network_information_from_instance_config(instance_name),
)
self._set_data_entry(
instance_name, "preferred_interface", self.get_prefered_instance_network_interface(instance_name)
)
self._set_data_entry(instance_name, "vlan_ids", self.get_instance_vlans(instance_name))
self._set_data_entry(
instance_name, "project", self._get_data_entry(f"instances/{instance_name}/instances/metadata/project")
)
def build_inventory_network(self, instance_name):
"""Add the network interfaces of the instance to the inventory
@ -653,30 +698,30 @@ class InventoryModule(BaseInventoryPlugin):
None
Returns:
dict(interface_name: ip)"""
prefered_interface = self._get_data_entry(f'inventory/{instance_name}/preferred_interface') # name or None
prefered_interface = self._get_data_entry(f"inventory/{instance_name}/preferred_interface") # name or None
prefered_instance_network_family = self.prefered_instance_network_family
ip_address = ''
ip_address = ""
if prefered_interface:
interface = self._get_data_entry(f'inventory/{instance_name}/network_interfaces/{prefered_interface}')
interface = self._get_data_entry(f"inventory/{instance_name}/network_interfaces/{prefered_interface}")
for config in interface:
if config['family'] == prefered_instance_network_family:
ip_address = config['address']
if config["family"] == prefered_instance_network_family:
ip_address = config["address"]
break
else:
interfaces = self._get_data_entry(f'inventory/{instance_name}/network_interfaces')
interfaces = self._get_data_entry(f"inventory/{instance_name}/network_interfaces")
for interface in interfaces.values():
for config in interface:
if config['family'] == prefered_instance_network_family:
ip_address = config['address']
if config["family"] == prefered_instance_network_family:
ip_address = config["address"]
break
return ip_address
if self._get_data_entry(f'inventory/{instance_name}/network_interfaces'): # instance have network interfaces
self.inventory.set_variable(instance_name, 'ansible_connection', 'ssh')
self.inventory.set_variable(instance_name, 'ansible_host', make_unsafe(interface_selection(instance_name)))
if self._get_data_entry(f"inventory/{instance_name}/network_interfaces"): # instance have network interfaces
self.inventory.set_variable(instance_name, "ansible_connection", "ssh")
self.inventory.set_variable(instance_name, "ansible_host", make_unsafe(interface_selection(instance_name)))
else:
self.inventory.set_variable(instance_name, 'ansible_connection', 'local')
self.inventory.set_variable(instance_name, "ansible_connection", "local")
def build_inventory_hosts(self):
"""Build host-part dynamic inventory
@ -692,8 +737,8 @@ class InventoryModule(BaseInventoryPlugin):
None
Returns:
None"""
for instance_name in self.data['inventory']:
instance_state = str(self._get_data_entry(f'inventory/{instance_name}/state') or "STOPPED").lower()
for instance_name in self.data["inventory"]:
instance_state = str(self._get_data_entry(f"inventory/{instance_name}/state") or "STOPPED").lower()
# Only consider instances that match the "state" filter, if self.state is not None
if self.filter:
@ -705,34 +750,47 @@ class InventoryModule(BaseInventoryPlugin):
# add network information
self.build_inventory_network(instance_name)
# add os
v = self._get_data_entry(f'inventory/{instance_name}/os')
v = self._get_data_entry(f"inventory/{instance_name}/os")
if v:
self.inventory.set_variable(instance_name, 'ansible_lxd_os', make_unsafe(v.lower()))
self.inventory.set_variable(instance_name, "ansible_lxd_os", make_unsafe(v.lower()))
# add release
v = self._get_data_entry(f'inventory/{instance_name}/release')
v = self._get_data_entry(f"inventory/{instance_name}/release")
if v:
self.inventory.set_variable(
instance_name, 'ansible_lxd_release', make_unsafe(v.lower()))
self.inventory.set_variable(instance_name, "ansible_lxd_release", make_unsafe(v.lower()))
# add profile
self.inventory.set_variable(
instance_name, 'ansible_lxd_profile', make_unsafe(self._get_data_entry(f'inventory/{instance_name}/profile')))
instance_name,
"ansible_lxd_profile",
make_unsafe(self._get_data_entry(f"inventory/{instance_name}/profile")),
)
# add state
self.inventory.set_variable(
instance_name, 'ansible_lxd_state', make_unsafe(instance_state))
self.inventory.set_variable(instance_name, "ansible_lxd_state", make_unsafe(instance_state))
# add type
self.inventory.set_variable(
instance_name, 'ansible_lxd_type', make_unsafe(self._get_data_entry(f'inventory/{instance_name}/type')))
instance_name, "ansible_lxd_type", make_unsafe(self._get_data_entry(f"inventory/{instance_name}/type"))
)
# add location information
if self._get_data_entry(f'inventory/{instance_name}/location') != "none": # wrong type by lxd 'none' != 'None'
if (
self._get_data_entry(f"inventory/{instance_name}/location") != "none"
): # wrong type by lxd 'none' != 'None'
self.inventory.set_variable(
instance_name, 'ansible_lxd_location', make_unsafe(self._get_data_entry(f'inventory/{instance_name}/location')))
instance_name,
"ansible_lxd_location",
make_unsafe(self._get_data_entry(f"inventory/{instance_name}/location")),
)
# add VLAN_ID information
if self._get_data_entry(f'inventory/{instance_name}/vlan_ids'):
if self._get_data_entry(f"inventory/{instance_name}/vlan_ids"):
self.inventory.set_variable(
instance_name, 'ansible_lxd_vlan_ids', make_unsafe(self._get_data_entry(f'inventory/{instance_name}/vlan_ids')))
instance_name,
"ansible_lxd_vlan_ids",
make_unsafe(self._get_data_entry(f"inventory/{instance_name}/vlan_ids")),
)
# add project
self.inventory.set_variable(
instance_name, 'ansible_lxd_project', make_unsafe(self._get_data_entry(f'inventory/{instance_name}/project')))
instance_name,
"ansible_lxd_project",
make_unsafe(self._get_data_entry(f"inventory/{instance_name}/project")),
)
def build_inventory_groups_location(self, group_name):
"""create group by attribute: location
@ -750,7 +808,7 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
for instance_name in self.inventory.hosts:
if 'ansible_lxd_location' in self.inventory.get_host(instance_name).get_vars():
if "ansible_lxd_location" in self.inventory.get_host(instance_name).get_vars():
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_pattern(self, group_name):
@ -768,7 +826,7 @@ class InventoryModule(BaseInventoryPlugin):
if group_name not in self.inventory.groups:
self.inventory.add_group(group_name)
regex_pattern = self.groupby[group_name].get('attribute')
regex_pattern = self.groupby[group_name].get("attribute")
for instance_name in self.inventory.hosts:
result = re.search(regex_pattern, instance_name)
@ -791,17 +849,18 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
try:
network = ipaddress.ip_network(to_text(self.groupby[group_name].get('attribute')))
network = ipaddress.ip_network(to_text(self.groupby[group_name].get("attribute")))
except ValueError as err:
raise AnsibleParserError(
f"Error while parsing network range {self.groupby[group_name].get('attribute')}: {err}")
f"Error while parsing network range {self.groupby[group_name].get('attribute')}: {err}"
)
for instance_name in self.inventory.hosts:
if self.data['inventory'][instance_name].get('network_interfaces') is not None:
for interface in self.data['inventory'][instance_name].get('network_interfaces'):
for interface_family in self.data['inventory'][instance_name].get('network_interfaces')[interface]:
if self.data["inventory"][instance_name].get("network_interfaces") is not None:
for interface in self.data["inventory"][instance_name].get("network_interfaces"):
for interface_family in self.data["inventory"][instance_name].get("network_interfaces")[interface]:
try:
address = ipaddress.ip_address(to_text(interface_family['address']))
address = ipaddress.ip_address(to_text(interface_family["address"]))
if address.version == network.version and address in network:
self.inventory.add_child(group_name, instance_name)
except ValueError:
@ -824,10 +883,14 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts
if 'ansible_lxd_project' in self.inventory.get_host(instance_name).get_vars()]
instance_name
for instance_name in self.inventory.hosts
if "ansible_lxd_project" in self.inventory.get_host(instance_name).get_vars()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_project'):
if self.groupby[group_name].get("attribute").lower() == self.inventory.get_host(
instance_name
).get_vars().get("ansible_lxd_project"):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_os(self, group_name):
@ -846,10 +909,14 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts
if 'ansible_lxd_os' in self.inventory.get_host(instance_name).get_vars()]
instance_name
for instance_name in self.inventory.hosts
if "ansible_lxd_os" in self.inventory.get_host(instance_name).get_vars()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_os'):
if self.groupby[group_name].get("attribute").lower() == self.inventory.get_host(
instance_name
).get_vars().get("ansible_lxd_os"):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_release(self, group_name):
@ -868,10 +935,14 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts
if 'ansible_lxd_release' in self.inventory.get_host(instance_name).get_vars()]
instance_name
for instance_name in self.inventory.hosts
if "ansible_lxd_release" in self.inventory.get_host(instance_name).get_vars()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_release'):
if self.groupby[group_name].get("attribute").lower() == self.inventory.get_host(
instance_name
).get_vars().get("ansible_lxd_release"):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_profile(self, group_name):
@ -890,10 +961,14 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts.keys()
if 'ansible_lxd_profile' in self.inventory.get_host(instance_name).get_vars().keys()]
instance_name
for instance_name in self.inventory.hosts.keys()
if "ansible_lxd_profile" in self.inventory.get_host(instance_name).get_vars().keys()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute').lower() in self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_profile'):
if self.groupby[group_name].get("attribute").lower() in self.inventory.get_host(
instance_name
).get_vars().get("ansible_lxd_profile"):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_vlanid(self, group_name):
@ -912,10 +987,15 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts.keys()
if 'ansible_lxd_vlan_ids' in self.inventory.get_host(instance_name).get_vars().keys()]
instance_name
for instance_name in self.inventory.hosts.keys()
if "ansible_lxd_vlan_ids" in self.inventory.get_host(instance_name).get_vars().keys()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute') in self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_vlan_ids').values():
if (
self.groupby[group_name].get("attribute")
in self.inventory.get_host(instance_name).get_vars().get("ansible_lxd_vlan_ids").values()
):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups_type(self, group_name):
@ -934,10 +1014,14 @@ class InventoryModule(BaseInventoryPlugin):
self.inventory.add_group(group_name)
gen_instances = [
instance_name for instance_name in self.inventory.hosts
if 'ansible_lxd_type' in self.inventory.get_host(instance_name).get_vars()]
instance_name
for instance_name in self.inventory.hosts
if "ansible_lxd_type" in self.inventory.get_host(instance_name).get_vars()
]
for instance_name in gen_instances:
if self.groupby[group_name].get('attribute').lower() == self.inventory.get_host(instance_name).get_vars().get('ansible_lxd_type'):
if self.groupby[group_name].get("attribute").lower() == self.inventory.get_host(
instance_name
).get_vars().get("ansible_lxd_type"):
self.inventory.add_child(group_name, instance_name)
def build_inventory_groups(self):
@ -980,31 +1064,31 @@ class InventoryModule(BaseInventoryPlugin):
None"""
# Due to the compatibility with python 2 no use of map
if self.groupby[group_name].get('type') == 'location':
if self.groupby[group_name].get("type") == "location":
self.build_inventory_groups_location(group_name)
elif self.groupby[group_name].get('type') == 'pattern':
elif self.groupby[group_name].get("type") == "pattern":
self.build_inventory_groups_pattern(group_name)
elif self.groupby[group_name].get('type') == 'network_range':
elif self.groupby[group_name].get("type") == "network_range":
self.build_inventory_groups_network_range(group_name)
elif self.groupby[group_name].get('type') == 'os':
elif self.groupby[group_name].get("type") == "os":
self.build_inventory_groups_os(group_name)
elif self.groupby[group_name].get('type') == 'release':
elif self.groupby[group_name].get("type") == "release":
self.build_inventory_groups_release(group_name)
elif self.groupby[group_name].get('type') == 'profile':
elif self.groupby[group_name].get("type") == "profile":
self.build_inventory_groups_profile(group_name)
elif self.groupby[group_name].get('type') == 'vlanid':
elif self.groupby[group_name].get("type") == "vlanid":
self.build_inventory_groups_vlanid(group_name)
elif self.groupby[group_name].get('type') == 'type':
elif self.groupby[group_name].get("type") == "type":
self.build_inventory_groups_type(group_name)
elif self.groupby[group_name].get('type') == 'project':
elif self.groupby[group_name].get("type") == "project":
self.build_inventory_groups_project(group_name)
else:
raise AnsibleParserError(f'Unknown group type: {to_native(group_name)}')
raise AnsibleParserError(f"Unknown group type: {to_native(group_name)}")
if self.groupby:
for group_name in self.groupby:
if not group_name.isalnum():
raise AnsibleParserError(f'Invalid character(s) in groupname: {to_native(group_name)}')
raise AnsibleParserError(f"Invalid character(s) in groupname: {to_native(group_name)}")
group_type(make_unsafe(group_name))
def build_inventory(self):
@ -1039,10 +1123,10 @@ class InventoryModule(BaseInventoryPlugin):
None
Returns:
None"""
iter_keys = list(self.data['instances'].keys())
iter_keys = list(self.data["instances"].keys())
for instance_name in iter_keys:
if self._get_data_entry(f'instances/{instance_name}/instances/metadata/type') != self.type_filter:
del self.data['instances'][instance_name]
if self._get_data_entry(f"instances/{instance_name}/instances/metadata/type") != self.type_filter:
del self.data["instances"][instance_name]
def _populate(self):
"""Return the hosts and groups
@ -1066,7 +1150,7 @@ class InventoryModule(BaseInventoryPlugin):
# The first version of the inventory only supported containers.
# This will change in the future.
# The following function cleans up the data.
if self.type_filter != 'both':
if self.type_filter != "both":
self.cleandata()
self.extract_information_from_instance_configs()
@ -1094,32 +1178,31 @@ class InventoryModule(BaseInventoryPlugin):
Returns:
None"""
if IPADDRESS_IMPORT_ERROR:
raise AnsibleError('another_library must be installed to use this plugin') from IPADDRESS_IMPORT_ERROR
raise AnsibleError("another_library must be installed to use this plugin") from IPADDRESS_IMPORT_ERROR
super().parse(inventory, loader, path, cache=False)
# Read the inventory YAML file
self._read_config_data(path)
try:
self.client_key = self.get_option('client_key')
self.client_cert = self.get_option('client_cert')
self.server_cert = self.get_option('server_cert')
self.server_check_hostname = self.get_option('server_check_hostname')
self.project = self.get_option('project')
self.client_key = self.get_option("client_key")
self.client_cert = self.get_option("client_cert")
self.server_cert = self.get_option("server_cert")
self.server_check_hostname = self.get_option("server_check_hostname")
self.project = self.get_option("project")
self.debug = self.DEBUG
self.data = {} # store for inventory-data
self.groupby = self.get_option('groupby')
self.plugin = self.get_option('plugin')
self.prefered_instance_network_family = self.get_option('prefered_instance_network_family')
self.prefered_instance_network_interface = self.get_option('prefered_instance_network_interface')
self.type_filter = self.get_option('type_filter')
if self.get_option('state').lower() == 'none': # none in config is str()
self.groupby = self.get_option("groupby")
self.plugin = self.get_option("plugin")
self.prefered_instance_network_family = self.get_option("prefered_instance_network_family")
self.prefered_instance_network_interface = self.get_option("prefered_instance_network_interface")
self.type_filter = self.get_option("type_filter")
if self.get_option("state").lower() == "none": # none in config is str()
self.filter = None
else:
self.filter = self.get_option('state').lower()
self.trust_password = self.get_option('trust_password')
self.url = self.get_option('url')
self.filter = self.get_option("state").lower()
self.trust_password = self.get_option("trust_password")
self.url = self.get_option("url")
except Exception as err:
raise AnsibleParserError(
f'All correct options required: {err}')
raise AnsibleParserError(f"All correct options required: {err}")
# Call our internal helper to populate the dynamic inventory
self._populate()

View file

@ -138,10 +138,9 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
NAME = 'community.general.nmap'
find_host = re.compile(r'^Nmap scan report for ([\w,.,-]+)(?: \(([\w,.,:,\[,\]]+)\))?')
find_port = re.compile(r'^(\d+)/(\w+)\s+(\w+)\s+(\w+)')
NAME = "community.general.nmap"
find_host = re.compile(r"^Nmap scan report for ([\w,.,-]+)(?: \(([\w,.,:,\[,\]]+)\))?")
find_port = re.compile(r"^(\d+)/(\w+)\s+(\w+)\s+(\w+)")
def __init__(self):
self._nmap = None
@ -149,26 +148,25 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def _populate(self, hosts):
# Use constructed if applicable
strict = self.get_option('strict')
strict = self.get_option("strict")
for host in hosts:
host = make_unsafe(host)
hostname = host['name']
hostname = host["name"]
self.inventory.add_host(hostname)
for var, value in host.items():
self.inventory.set_variable(hostname, var, value)
# Composed variables
self._set_composite_vars(self.get_option('compose'), host, hostname, strict=strict)
self._set_composite_vars(self.get_option("compose"), host, hostname, strict=strict)
# Complex groups based on jinja2 conditionals, hosts that meet the conditional are added to group
self._add_host_to_composed_groups(self.get_option('groups'), host, hostname, strict=strict)
self._add_host_to_composed_groups(self.get_option("groups"), host, hostname, strict=strict)
# Create groups based on variable values and add the corresponding hosts to it
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), host, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), host, hostname, strict=strict)
def verify_file(self, path):
valid = False
if super().verify_file(path):
file_name, ext = os.path.splitext(path)
@ -179,11 +177,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return valid
def parse(self, inventory, loader, path, cache=True):
try:
self._nmap = get_bin_path('nmap')
self._nmap = get_bin_path("nmap")
except ValueError as e:
raise AnsibleParserError(f'nmap inventory plugin requires the nmap cli tool to work: {e}')
raise AnsibleParserError(f"nmap inventory plugin requires the nmap cli tool to work: {e}")
super().parse(inventory, loader, path, cache=cache)
@ -193,7 +190,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# cache may be True or False at this point to indicate if the inventory is being refreshed
# get the user's cache option too to see if we should save the cache if it is changing
user_cache_setting = self.get_option('cache')
user_cache_setting = self.get_option("cache")
# read if the user has caching enabled and the cache isn't being refreshed
attempt_to_read_cache = user_cache_setting and cache
@ -211,53 +208,53 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# setup command
cmd = [self._nmap]
if self.get_option('sudo'):
cmd.insert(0, 'sudo')
if self.get_option("sudo"):
cmd.insert(0, "sudo")
if self.get_option('port'):
cmd.append('-p')
cmd.append(self.get_option('port'))
if self.get_option("port"):
cmd.append("-p")
cmd.append(self.get_option("port"))
if not self.get_option('ports'):
cmd.append('-sP')
if not self.get_option("ports"):
cmd.append("-sP")
if self.get_option('ipv4') and not self.get_option('ipv6'):
cmd.append('-4')
elif self.get_option('ipv6') and not self.get_option('ipv4'):
cmd.append('-6')
elif not self.get_option('ipv6') and not self.get_option('ipv4'):
raise AnsibleParserError('One of ipv4 or ipv6 must be enabled for this plugin')
if self.get_option("ipv4") and not self.get_option("ipv6"):
cmd.append("-4")
elif self.get_option("ipv6") and not self.get_option("ipv4"):
cmd.append("-6")
elif not self.get_option("ipv6") and not self.get_option("ipv4"):
raise AnsibleParserError("One of ipv4 or ipv6 must be enabled for this plugin")
if self.get_option('exclude'):
cmd.append('--exclude')
cmd.append(','.join(self.get_option('exclude')))
if self.get_option("exclude"):
cmd.append("--exclude")
cmd.append(",".join(self.get_option("exclude")))
if self.get_option('dns_resolve'):
cmd.append('-n')
if self.get_option("dns_resolve"):
cmd.append("-n")
if self.get_option('dns_servers'):
cmd.append('--dns-servers')
cmd.append(','.join(self.get_option('dns_servers')))
if self.get_option("dns_servers"):
cmd.append("--dns-servers")
cmd.append(",".join(self.get_option("dns_servers")))
if self.get_option('udp_scan'):
cmd.append('-sU')
if self.get_option("udp_scan"):
cmd.append("-sU")
if self.get_option('icmp_timestamp'):
cmd.append('-PP')
if self.get_option("icmp_timestamp"):
cmd.append("-PP")
if self.get_option('open'):
cmd.append('--open')
if self.get_option("open"):
cmd.append("--open")
if not self.get_option('use_arp_ping'):
cmd.append('--disable-arp-ping')
if not self.get_option("use_arp_ping"):
cmd.append("--disable-arp-ping")
cmd.append(self.get_option('address'))
cmd.append(self.get_option("address"))
try:
# execute
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise AnsibleParserError(f'Failed to run nmap, rc={p.returncode}: {to_native(stderr)}')
raise AnsibleParserError(f"Failed to run nmap, rc={p.returncode}: {to_native(stderr)}")
# parse results
host = None
@ -266,18 +263,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
results = []
try:
t_stdout = to_text(stdout, errors='surrogate_or_strict')
t_stdout = to_text(stdout, errors="surrogate_or_strict")
except UnicodeError as e:
raise AnsibleParserError(f'Invalid (non unicode) input returned: {e}')
raise AnsibleParserError(f"Invalid (non unicode) input returned: {e}")
for line in t_stdout.splitlines():
hits = self.find_host.match(line)
if hits:
if host is not None and ports:
results[-1]['ports'] = ports
results[-1]["ports"] = ports
# if dns only shows arpa, just use ip instead as hostname
if hits.group(1).endswith('.in-addr.arpa'):
if hits.group(1).endswith(".in-addr.arpa"):
host = hits.group(2)
else:
host = hits.group(1)
@ -291,22 +288,26 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
if host is not None:
# update inventory
results.append(dict())
results[-1]['name'] = host
results[-1]['ip'] = ip
results[-1]["name"] = host
results[-1]["ip"] = ip
ports = []
continue
host_ports = self.find_port.match(line)
if host is not None and host_ports:
ports.append({'port': host_ports.group(1),
'protocol': host_ports.group(2),
'state': host_ports.group(3),
'service': host_ports.group(4)})
ports.append(
{
"port": host_ports.group(1),
"protocol": host_ports.group(2),
"state": host_ports.group(3),
"service": host_ports.group(4),
}
)
continue
# if any leftovers
if host and ports:
results[-1]['ports'] = ports
results[-1]["ports"] = ports
except Exception as e:
raise AnsibleParserError(f"failed to parse {to_native(path)}: {e} ")

View file

@ -73,7 +73,7 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
class InventoryModule(BaseInventoryPlugin):
NAME = 'community.general.online'
NAME = "community.general.online"
API_ENDPOINT = "https://api.online.net"
def extract_public_ipv4(self, host_infos):
@ -140,7 +140,7 @@ class InventoryModule(BaseInventoryPlugin):
return None
try:
raw_data = to_text(response.read(), errors='surrogate_or_strict')
raw_data = to_text(response.read(), errors="surrogate_or_strict")
except UnicodeError:
raise AnsibleError("Incorrect encoding of fetched payload from Online servers")
@ -168,26 +168,33 @@ class InventoryModule(BaseInventoryPlugin):
"last_reboot",
"anti_ddos",
"hardware_watch",
"support"
"support",
)
for attribute in targeted_attributes:
self.inventory.set_variable(hostname, attribute, make_unsafe(host_infos[attribute]))
if self.extract_public_ipv4(host_infos=host_infos):
self.inventory.set_variable(hostname, "public_ipv4", make_unsafe(self.extract_public_ipv4(host_infos=host_infos)))
self.inventory.set_variable(hostname, "ansible_host", make_unsafe(self.extract_public_ipv4(host_infos=host_infos)))
self.inventory.set_variable(
hostname, "public_ipv4", make_unsafe(self.extract_public_ipv4(host_infos=host_infos))
)
self.inventory.set_variable(
hostname, "ansible_host", make_unsafe(self.extract_public_ipv4(host_infos=host_infos))
)
if self.extract_private_ipv4(host_infos=host_infos):
self.inventory.set_variable(hostname, "public_ipv4", make_unsafe(self.extract_private_ipv4(host_infos=host_infos)))
self.inventory.set_variable(
hostname, "public_ipv4", make_unsafe(self.extract_private_ipv4(host_infos=host_infos))
)
if self.extract_os_name(host_infos=host_infos):
self.inventory.set_variable(hostname, "os_name", make_unsafe(self.extract_os_name(host_infos=host_infos)))
if self.extract_os_version(host_infos=host_infos):
self.inventory.set_variable(hostname, "os_version", make_unsafe(self.extract_os_name(host_infos=host_infos)))
self.inventory.set_variable(
hostname, "os_version", make_unsafe(self.extract_os_name(host_infos=host_infos))
)
def _filter_host(self, host_infos, hostname_preferences):
for pref in hostname_preferences:
if self.extractors[pref](host_infos):
return self.extractors[pref](host_infos)
@ -195,9 +202,7 @@ class InventoryModule(BaseInventoryPlugin):
return None
def do_server_inventory(self, host_infos, hostname_preferences, group_preferences):
hostname = self._filter_host(host_infos=host_infos,
hostname_preferences=hostname_preferences)
hostname = self._filter_host(host_infos=host_infos, hostname_preferences=hostname_preferences)
# No suitable hostname were found in the attributes and the host won't be in the inventory
if not hostname:
@ -239,13 +244,13 @@ class InventoryModule(BaseInventoryPlugin):
self.group_extractors = {
"location": self.extract_location,
"offer": self.extract_offer,
"rpn": self.extract_rpn
"rpn": self.extract_rpn,
}
self.headers = {
'Authorization': f"Bearer {token}",
'User-Agent': f"ansible {ansible_version} Python {python_version.split(' ', 1)[0]}",
'Content-type': 'application/json'
"Authorization": f"Bearer {token}",
"User-Agent": f"ansible {ansible_version} Python {python_version.split(' ', 1)[0]}",
"Content-type": "application/json",
}
servers_url = urljoin(InventoryModule.API_ENDPOINT, "api/v1/server")
@ -257,13 +262,14 @@ class InventoryModule(BaseInventoryPlugin):
self.rpn_lookup_cache = self.extract_rpn_lookup_cache(rpn_list)
for server_api_path in servers_api_path:
server_url = urljoin(InventoryModule.API_ENDPOINT, server_api_path)
raw_server_info = self._fetch_information(url=server_url)
if raw_server_info is None:
continue
self.do_server_inventory(host_infos=raw_server_info,
hostname_preferences=hostname_preferences,
group_preferences=group_preferences)
self.do_server_inventory(
host_infos=raw_server_info,
hostname_preferences=hostname_preferences,
group_preferences=group_preferences,
)

View file

@ -100,20 +100,20 @@ import os
class InventoryModule(BaseInventoryPlugin, Constructable):
NAME = 'community.general.opennebula'
NAME = "community.general.opennebula"
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('opennebula.yaml', 'opennebula.yml')):
if path.endswith(("opennebula.yaml", "opennebula.yml")):
valid = True
return valid
def _get_connection_info(self):
url = self.get_option('api_url')
username = self.get_option('api_username')
password = self.get_option('api_password')
authfile = self.get_option('api_authfile')
url = self.get_option("api_url")
username = self.get_option("api_username")
password = self.get_option("api_password")
authfile = self.get_option("api_authfile")
if not username and not password:
if authfile is None:
@ -127,31 +127,31 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
except Exception:
raise AnsibleError(f"Error occurs when reading ONE_AUTH file at '{authfile}'")
auth_params = namedtuple('auth', ('url', 'username', 'password'))
auth_params = namedtuple("auth", ("url", "username", "password"))
return auth_params(url=url, username=username, password=password)
def _get_vm_ipv4(self, vm):
nic = vm.TEMPLATE.get('NIC')
nic = vm.TEMPLATE.get("NIC")
if isinstance(nic, dict):
nic = [nic]
for net in nic:
if net.get('IP'):
return net['IP']
if net.get("IP"):
return net["IP"]
return False
def _get_vm_ipv6(self, vm):
nic = vm.TEMPLATE.get('NIC')
nic = vm.TEMPLATE.get("NIC")
if isinstance(nic, dict):
nic = [nic]
for net in nic:
if net.get('IP6_GLOBAL'):
return net['IP6_GLOBAL']
if net.get("IP6_GLOBAL"):
return net["IP6_GLOBAL"]
return False
@ -159,7 +159,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
auth = self._get_connection_info()
if not (auth.username and auth.password):
raise AnsibleError('API Credentials missing. Check OpenNebula inventory file.')
raise AnsibleError("API Credentials missing. Check OpenNebula inventory file.")
else:
one_client = pyone.OneServer(auth.url, session=f"{auth.username}:{auth.password}")
@ -181,72 +181,74 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
server = vm.USER_TEMPLATE
labels = []
if vm.USER_TEMPLATE.get('LABELS'):
labels = [s for s in vm.USER_TEMPLATE.get('LABELS') if s == ',' or s == '-' or s.isalnum() or s.isspace()]
labels = ''.join(labels)
labels = labels.replace(' ', '_')
labels = labels.replace('-', '_')
labels = labels.split(',')
if vm.USER_TEMPLATE.get("LABELS"):
labels = [
s for s in vm.USER_TEMPLATE.get("LABELS") if s == "," or s == "-" or s.isalnum() or s.isspace()
]
labels = "".join(labels)
labels = labels.replace(" ", "_")
labels = labels.replace("-", "_")
labels = labels.split(",")
# filter by label
if label_filter is not None:
if label_filter not in labels:
continue
server['name'] = vm.NAME
server['id'] = vm.ID
if hasattr(vm.HISTORY_RECORDS, 'HISTORY') and vm.HISTORY_RECORDS.HISTORY:
server['host'] = vm.HISTORY_RECORDS.HISTORY[-1].HOSTNAME
server['LABELS'] = labels
server['v4_first_ip'] = self._get_vm_ipv4(vm)
server['v6_first_ip'] = self._get_vm_ipv6(vm)
server["name"] = vm.NAME
server["id"] = vm.ID
if hasattr(vm.HISTORY_RECORDS, "HISTORY") and vm.HISTORY_RECORDS.HISTORY:
server["host"] = vm.HISTORY_RECORDS.HISTORY[-1].HOSTNAME
server["LABELS"] = labels
server["v4_first_ip"] = self._get_vm_ipv4(vm)
server["v6_first_ip"] = self._get_vm_ipv6(vm)
result.append(server)
return result
def _populate(self):
hostname_preference = self.get_option('hostname')
group_by_labels = self.get_option('group_by_labels')
strict = self.get_option('strict')
hostname_preference = self.get_option("hostname")
group_by_labels = self.get_option("group_by_labels")
strict = self.get_option("strict")
# Add a top group 'one'
self.inventory.add_group(group='all')
self.inventory.add_group(group="all")
filter_by_label = self.get_option('filter_by_label')
filter_by_label = self.get_option("filter_by_label")
servers = self._retrieve_servers(filter_by_label)
for server in servers:
server = make_unsafe(server)
hostname = server['name']
hostname = server["name"]
# check for labels
if group_by_labels and server['LABELS']:
for label in server['LABELS']:
if group_by_labels and server["LABELS"]:
for label in server["LABELS"]:
self.inventory.add_group(group=label)
self.inventory.add_host(host=hostname, group=label)
self.inventory.add_host(host=hostname, group='all')
self.inventory.add_host(host=hostname, group="all")
for attribute, value in server.items():
self.inventory.set_variable(hostname, attribute, value)
if hostname_preference != 'name':
self.inventory.set_variable(hostname, 'ansible_host', server[hostname_preference])
if hostname_preference != "name":
self.inventory.set_variable(hostname, "ansible_host", server[hostname_preference])
if server.get('SSH_PORT'):
self.inventory.set_variable(hostname, 'ansible_port', server['SSH_PORT'])
if server.get("SSH_PORT"):
self.inventory.set_variable(hostname, "ansible_port", server["SSH_PORT"])
# handle construcable implementation: get composed variables if any
self._set_composite_vars(self.get_option('compose'), server, hostname, strict=strict)
self._set_composite_vars(self.get_option("compose"), server, hostname, strict=strict)
# groups based on jinja conditionals get added to specific groups
self._add_host_to_composed_groups(self.get_option('groups'), server, hostname, strict=strict)
self._add_host_to_composed_groups(self.get_option("groups"), server, hostname, strict=strict)
# groups based on variables associated with them in the inventory
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), server, hostname, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), server, hostname, strict=strict)
def parse(self, inventory, loader, path, cache=True):
if not HAS_PYONE:
raise AnsibleError('OpenNebula Inventory plugin requires pyone to work!')
raise AnsibleError("OpenNebula Inventory plugin requires pyone to work!")
super().parse(inventory, loader, path)
self._read_config_data(path=path)

View file

@ -141,9 +141,7 @@ def _fetch_information(token, url):
paginated_url = url
while True:
try:
response = open_url(paginated_url,
headers={'X-Auth-Token': token,
'Content-type': 'application/json'})
response = open_url(paginated_url, headers={"X-Auth-Token": token, "Content-type": "application/json"})
except Exception as e:
raise AnsibleError(f"Error while fetching {url}: {e}")
try:
@ -156,13 +154,13 @@ def _fetch_information(token, url):
except KeyError:
raise AnsibleError("Incorrect format from the Scaleway API response")
link = response.headers['Link']
link = response.headers["Link"]
if not link:
return results
relations = parse_pagination_link(link)
if 'next' not in relations:
if "next" not in relations:
return results
paginated_url = urllib_parse.urljoin(paginated_url, relations['next'])
paginated_url = urllib_parse.urljoin(paginated_url, relations["next"])
def _build_server_url(api_endpoint):
@ -223,12 +221,12 @@ extractors = {
"private_ipv4": extract_private_ipv4,
"public_ipv6": extract_public_ipv6,
"hostname": extract_hostname,
"id": extract_server_id
"id": extract_server_id,
}
class InventoryModule(BaseInventoryPlugin, Constructable):
NAME = 'community.general.scaleway'
NAME = "community.general.scaleway"
def _fill_host_variables(self, host, server_info):
targeted_attributes = (
@ -275,7 +273,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
return matching_tags.union((server_zone,))
def _filter_host(self, host_infos, hostname_preferences):
for pref in hostname_preferences:
if extractors[pref](host_infos):
return extractors[pref](host_infos)
@ -290,9 +287,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
raw_zone_hosts_infos = make_unsafe(_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)
hostname = self._filter_host(host_infos=host_infos, hostname_preferences=hostname_preferences)
# No suitable hostname were found in the attributes and the host won't be in the inventory
if not hostname:
@ -306,38 +301,38 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
self._fill_host_variables(host=hostname, server_info=host_infos)
# Composed variables
self._set_composite_vars(self.get_option('variables'), host_infos, hostname, strict=False)
self._set_composite_vars(self.get_option("variables"), host_infos, hostname, strict=False)
def get_oauth_token(self):
oauth_token = self.get_option('oauth_token')
oauth_token = self.get_option("oauth_token")
if 'SCW_CONFIG_PATH' in os.environ:
scw_config_path = os.getenv('SCW_CONFIG_PATH')
elif 'XDG_CONFIG_HOME' in os.environ:
scw_config_path = os.path.join(os.getenv('XDG_CONFIG_HOME'), 'scw', 'config.yaml')
if "SCW_CONFIG_PATH" in os.environ:
scw_config_path = os.getenv("SCW_CONFIG_PATH")
elif "XDG_CONFIG_HOME" in os.environ:
scw_config_path = os.path.join(os.getenv("XDG_CONFIG_HOME"), "scw", "config.yaml")
else:
scw_config_path = os.path.join(os.path.expanduser('~'), '.config', 'scw', 'config.yaml')
scw_config_path = os.path.join(os.path.expanduser("~"), ".config", "scw", "config.yaml")
if not oauth_token and os.path.exists(scw_config_path):
with open(scw_config_path) as fh:
scw_config = yaml.safe_load(fh)
ansible_profile = self.get_option('scw_profile')
ansible_profile = self.get_option("scw_profile")
if ansible_profile:
active_profile = ansible_profile
else:
active_profile = scw_config.get('active_profile', 'default')
active_profile = scw_config.get("active_profile", "default")
if active_profile == 'default':
oauth_token = scw_config.get('secret_key')
if active_profile == "default":
oauth_token = scw_config.get("secret_key")
else:
oauth_token = scw_config['profiles'][active_profile].get('secret_key')
oauth_token = scw_config["profiles"][active_profile].get("secret_key")
return oauth_token
def parse(self, inventory, loader, path, cache=True):
if YAML_IMPORT_ERROR:
raise AnsibleError('PyYAML is probably missing') from YAML_IMPORT_ERROR
raise AnsibleError("PyYAML is probably missing") from YAML_IMPORT_ERROR
super().parse(inventory, loader, path)
self._read_config_data(path=path)
@ -345,8 +340,12 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
tags = self.get_option("tags")
token = self.get_oauth_token()
if not token:
raise AnsibleError("'oauth_token' value is null, you must configure it either in inventory, envvars or scaleway-cli config.")
raise AnsibleError(
"'oauth_token' value is null, you must configure it either in inventory, envvars or scaleway-cli config."
)
hostname_preference = self.get_option("hostnames")
for zone in self._get_zones(config_zones):
self.do_zone_inventory(zone=make_unsafe(zone), token=token, tags=tags, hostname_preferences=hostname_preference)
self.do_zone_inventory(
zone=make_unsafe(zone), token=token, tags=tags, hostname_preferences=hostname_preference
)

View file

@ -85,9 +85,9 @@ from ansible_collections.community.general.plugins.plugin_utils.unsafe import ma
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
''' Host inventory parser for ansible using local virtualbox. '''
"""Host inventory parser for ansible using local virtualbox."""
NAME = 'community.general.virtualbox'
NAME = "community.general.virtualbox"
VBOX = "VBoxManage"
def __init__(self):
@ -97,56 +97,58 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def _query_vbox_data(self, host, property_path):
ret = None
try:
cmd = [self._vbox_path, b'guestproperty', b'get',
to_bytes(host, errors='surrogate_or_strict'),
to_bytes(property_path, errors='surrogate_or_strict')]
cmd = [
self._vbox_path,
b"guestproperty",
b"get",
to_bytes(host, errors="surrogate_or_strict"),
to_bytes(property_path, errors="surrogate_or_strict"),
]
x = Popen(cmd, stdout=PIPE)
ipinfo = to_text(x.stdout.read(), errors='surrogate_or_strict')
if 'Value' in ipinfo:
a, ip = ipinfo.split(':', 1)
ipinfo = to_text(x.stdout.read(), errors="surrogate_or_strict")
if "Value" in ipinfo:
a, ip = ipinfo.split(":", 1)
ret = ip.strip()
except Exception:
pass
return ret
def _set_variables(self, hostvars):
# set vars in inventory from hostvars
for host in hostvars:
query = self.get_option('query')
query = self.get_option("query")
# create vars from vbox properties
if query and isinstance(query, MutableMapping):
for varname in query:
hostvars[host][varname] = self._query_vbox_data(host, query[varname])
strict = self.get_option('strict')
strict = self.get_option("strict")
# create composite vars
self._set_composite_vars(self.get_option('compose'), hostvars[host], host, strict=strict)
self._set_composite_vars(self.get_option("compose"), hostvars[host], host, strict=strict)
# actually update inventory
for key in hostvars[host]:
self.inventory.set_variable(host, key, hostvars[host][key])
# constructed groups based on conditionals
self._add_host_to_composed_groups(self.get_option('groups'), hostvars[host], host, strict=strict)
self._add_host_to_composed_groups(self.get_option("groups"), hostvars[host], host, strict=strict)
# constructed keyed_groups
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), hostvars[host], host, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), hostvars[host], host, strict=strict)
def _populate_from_cache(self, source_data):
source_data = make_unsafe(source_data)
hostvars = source_data.pop('_meta', {}).get('hostvars', {})
hostvars = source_data.pop("_meta", {}).get("hostvars", {})
for group in source_data:
if group == 'all':
if group == "all":
continue
else:
group = self.inventory.add_group(group)
hosts = source_data[group].get('hosts', [])
hosts = source_data[group].get("hosts", [])
for host in hosts:
self._populate_host_vars([host], hostvars.get(host, {}), group)
self.inventory.add_child('all', group)
self.inventory.add_child("all", group)
if not source_data:
for host in hostvars:
self.inventory.add_host(host)
@ -157,32 +159,32 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._populate_from_cache(source_data)
return source_data
cacheable_results = {'_meta': {'hostvars': {}}}
cacheable_results = {"_meta": {"hostvars": {}}}
hostvars = {}
prevkey = pref_k = ''
prevkey = pref_k = ""
current_host = None
# needed to possibly set ansible_host
netinfo = self.get_option('network_info_path')
netinfo = self.get_option("network_info_path")
for line in source_data:
line = to_text(line)
if ':' not in line:
if ":" not in line:
continue
try:
k, v = line.split(':', 1)
k, v = line.split(":", 1)
except Exception:
# skip non splitable
continue
if k.strip() == '':
if k.strip() == "":
# skip empty
continue
v = v.strip()
# found host
if k.startswith('Name') and ',' not in v: # some setting strings appear in Name
if k.startswith("Name") and "," not in v: # some setting strings appear in Name
current_host = make_unsafe(v)
if current_host not in hostvars:
hostvars[current_host] = {}
@ -191,11 +193,11 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# try to get network info
netdata = self._query_vbox_data(current_host, netinfo)
if netdata:
self.inventory.set_variable(current_host, 'ansible_host', make_unsafe(netdata))
self.inventory.set_variable(current_host, "ansible_host", make_unsafe(netdata))
# found groups
elif k == 'Groups':
if self.get_option('enable_advanced_group_parsing'):
elif k == "Groups":
if self.get_option("enable_advanced_group_parsing"):
self._handle_vboxmanage_group_string(v, current_host, cacheable_results)
else:
self._handle_group_string(v, current_host, cacheable_results)
@ -204,7 +206,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
else:
# found vars, accumulate in hostvars for clean inventory set
pref_k = make_unsafe(f"vbox_{k.strip().replace(' ', '_')}")
leading_spaces = len(k) - len(k.lstrip(' '))
leading_spaces = len(k) - len(k.lstrip(" "))
if 0 < leading_spaces <= 2:
if prevkey not in hostvars[current_host] or not isinstance(hostvars[current_host][prevkey], dict):
hostvars[current_host][prevkey] = {}
@ -212,26 +214,26 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
elif leading_spaces > 2:
continue
else:
if v != '':
if v != "":
hostvars[current_host][pref_k] = make_unsafe(v)
if self._ungrouped_host(current_host, cacheable_results):
if 'ungrouped' not in cacheable_results:
cacheable_results['ungrouped'] = {'hosts': []}
cacheable_results['ungrouped']['hosts'].append(current_host)
if "ungrouped" not in cacheable_results:
cacheable_results["ungrouped"] = {"hosts": []}
cacheable_results["ungrouped"]["hosts"].append(current_host)
prevkey = pref_k
self._set_variables(hostvars)
for host in hostvars:
h = self.inventory.get_host(host)
cacheable_results['_meta']['hostvars'][h.name] = h.vars
cacheable_results["_meta"]["hostvars"][h.name] = h.vars
return cacheable_results
def _ungrouped_host(self, host, inventory):
def find_host(host, inventory):
for k, v in inventory.items():
if k == '_meta':
if k == "_meta":
continue
if isinstance(v, dict):
yield self._ungrouped_host(host, v)
@ -242,20 +244,20 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return all(find_host(host, inventory))
def _handle_group_string(self, vboxmanage_group, current_host, cacheable_results):
'''Handles parsing the VM's Group assignment from VBoxManage according to this inventory's initial implementation.'''
"""Handles parsing the VM's Group assignment from VBoxManage according to this inventory's initial implementation."""
# The original implementation of this inventory plugin treated `/` as
# a delimeter to split and use as Ansible Groups.
for group in vboxmanage_group.split('/'):
for group in vboxmanage_group.split("/"):
if group:
group = make_unsafe(group)
group = self.inventory.add_group(group)
self.inventory.add_child(group, current_host)
if group not in cacheable_results:
cacheable_results[group] = {'hosts': []}
cacheable_results[group]['hosts'].append(current_host)
cacheable_results[group] = {"hosts": []}
cacheable_results[group]["hosts"].append(current_host)
def _handle_vboxmanage_group_string(self, vboxmanage_group, current_host, cacheable_results):
'''Handles parsing the VM's Group assignment from VBoxManage according to VirtualBox documentation.'''
"""Handles parsing the VM's Group assignment from VBoxManage according to VirtualBox documentation."""
# Per the VirtualBox documentation, a VM can be part of many groups,
# and it is possible to have nested groups.
# Many groups are separated by commas ",", and nested groups use
@ -264,7 +266,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# Multi groups: VBoxManage modifyvm "vm01" --groups "/TestGroup,/TestGroup2"
# Nested groups: VBoxManage modifyvm "vm01" --groups "/TestGroup/TestGroup2"
for group in vboxmanage_group.split(','):
for group in vboxmanage_group.split(","):
if not group:
# We could get an empty element due how to split works, and
# possible assignments from VirtualBox. e.g. ,/Group1
@ -277,13 +279,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
continue
parent_group = None
for subgroup in group.split('/'):
for subgroup in group.split("/"):
if not subgroup:
# Similarly to above, we could get an empty element.
# e.g //Group1
continue
if subgroup == '/':
if subgroup == "/":
# "root" group.
# Consider the host to be unassigned
continue
@ -294,21 +296,19 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self.inventory.add_child(parent_group, subgroup)
self.inventory.add_child(subgroup, current_host)
if subgroup not in cacheable_results:
cacheable_results[subgroup] = {'hosts': []}
cacheable_results[subgroup]['hosts'].append(current_host)
cacheable_results[subgroup] = {"hosts": []}
cacheable_results[subgroup]["hosts"].append(current_host)
parent_group = subgroup
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('virtualbox.yaml', 'virtualbox.yml', 'vbox.yaml', 'vbox.yml')):
if path.endswith(("virtualbox.yaml", "virtualbox.yml", "vbox.yaml", "vbox.yml")):
valid = True
return valid
def parse(self, inventory, loader, path, cache=True):
try:
self._vbox_path = get_bin_path(self.VBOX)
except ValueError as e:
@ -325,7 +325,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
source_data = None
if cache:
cache = self.get_option('cache')
cache = self.get_option("cache")
update_cache = False
if cache:
@ -335,18 +335,20 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
update_cache = True
if not source_data:
b_pwfile = to_bytes(self.get_option('settings_password_file'), errors='surrogate_or_strict', nonstring='passthru')
running = self.get_option('running_only')
b_pwfile = to_bytes(
self.get_option("settings_password_file"), errors="surrogate_or_strict", nonstring="passthru"
)
running = self.get_option("running_only")
# start getting data
cmd = [self._vbox_path, b'list', b'-l']
cmd = [self._vbox_path, b"list", b"-l"]
if running:
cmd.append(b'runningvms')
cmd.append(b"runningvms")
else:
cmd.append(b'vms')
cmd.append(b"vms")
if b_pwfile and os.path.exists(b_pwfile):
cmd.append(b'--settingspwfile')
cmd.append(b"--settingspwfile")
cmd.append(b_pwfile)
try:

View file

@ -110,32 +110,31 @@ try:
import websocket
from websocket import create_connection
if LooseVersion(websocket.__version__) <= LooseVersion('1.0.0'):
if LooseVersion(websocket.__version__) <= LooseVersion("1.0.0"):
raise ImportError
except ImportError as e:
HAS_WEBSOCKET = False
HALTED = 'Halted'
PAUSED = 'Paused'
RUNNING = 'Running'
SUSPENDED = 'Suspended'
HALTED = "Halted"
PAUSED = "Paused"
RUNNING = "Running"
SUSPENDED = "Suspended"
POWER_STATES = [RUNNING, HALTED, SUSPENDED, PAUSED]
HOST_GROUP = 'xo_hosts'
POOL_GROUP = 'xo_pools'
HOST_GROUP = "xo_hosts"
POOL_GROUP = "xo_pools"
def clean_group_name(label):
return label.lower().replace(' ', '-').replace('-', '_')
return label.lower().replace(" ", "-").replace("-", "_")
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
''' Host inventory parser for ansible using XenOrchestra as source. '''
"""Host inventory parser for ansible using XenOrchestra as source."""
NAME = 'community.general.xen_orchestra'
NAME = "community.general.xen_orchestra"
def __init__(self):
super().__init__()
# from config
@ -150,13 +149,12 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
return self.counter
def create_connection(self, xoa_api_host):
validate_certs = self.get_option('validate_certs')
use_ssl = self.get_option('use_ssl')
proto = 'wss' if use_ssl else 'ws'
validate_certs = self.get_option("validate_certs")
use_ssl = self.get_option("use_ssl")
proto = "wss" if use_ssl else "ws"
sslopt = None if validate_certs else {'cert_reqs': ssl.CERT_NONE}
self.conn = create_connection(
f'{proto}://{xoa_api_host}/api/', sslopt=sslopt)
sslopt = None if validate_certs else {"cert_reqs": ssl.CERT_NONE}
self.conn = create_connection(f"{proto}://{xoa_api_host}/api/", sslopt=sslopt)
CALL_TIMEOUT = 100
"""Number of 1/10ths of a second to wait before method call times out."""
@ -164,74 +162,67 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
def call(self, method, params):
"""Calls a method on the XO server with the provided parameters."""
id = self.pointer
self.conn.send(json.dumps({
'id': id,
'jsonrpc': '2.0',
'method': method,
'params': params
}))
self.conn.send(json.dumps({"id": id, "jsonrpc": "2.0", "method": method, "params": params}))
waited = 0
while waited < self.CALL_TIMEOUT:
response = json.loads(self.conn.recv())
if 'id' in response and response['id'] == id:
if "id" in response and response["id"] == id:
return response
else:
sleep(0.1)
waited += 1
raise AnsibleError(f'Method call {method} timed out after {self.CALL_TIMEOUT / 10} seconds.')
raise AnsibleError(f"Method call {method} timed out after {self.CALL_TIMEOUT / 10} seconds.")
def login(self, user, password):
result = self.call('session.signIn', {
'username': user, 'password': password
})
result = self.call("session.signIn", {"username": user, "password": password})
if 'error' in result:
if "error" in result:
raise AnsibleError(f"Could not connect: {result['error']}")
def get_object(self, name):
answer = self.call('xo.getAllObjects', {'filter': {'type': name}})
answer = self.call("xo.getAllObjects", {"filter": {"type": name}})
if 'error' in answer:
if "error" in answer:
raise AnsibleError(f"Could not request: {answer['error']}")
return answer['result']
return answer["result"]
def _get_objects(self):
self.create_connection(self.xoa_api_host)
self.login(self.xoa_user, self.xoa_password)
return {
'vms': self.get_object('VM'),
'pools': self.get_object('pool'),
'hosts': self.get_object('host'),
"vms": self.get_object("VM"),
"pools": self.get_object("pool"),
"hosts": self.get_object("host"),
}
def _apply_constructable(self, name, variables):
strict = self.get_option('strict')
self._add_host_to_composed_groups(self.get_option('groups'), variables, name, strict=strict)
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), variables, name, strict=strict)
self._set_composite_vars(self.get_option('compose'), variables, name, strict=strict)
strict = self.get_option("strict")
self._add_host_to_composed_groups(self.get_option("groups"), variables, name, strict=strict)
self._add_host_to_keyed_groups(self.get_option("keyed_groups"), variables, name, strict=strict)
self._set_composite_vars(self.get_option("compose"), variables, name, strict=strict)
def _add_vms(self, vms, hosts, pools):
vm_name_list = []
for uuid, vm in vms.items():
if self.vm_entry_name_type == 'name_label':
if vm['name_label'] not in vm_name_list:
entry_name = vm['name_label']
vm_name_list.append(vm['name_label'])
if self.vm_entry_name_type == "name_label":
if vm["name_label"] not in vm_name_list:
entry_name = vm["name_label"]
vm_name_list.append(vm["name_label"])
else:
vm_duplicate_count = vm_name_list.count(vm['name_label'])
vm_duplicate_count = vm_name_list.count(vm["name_label"])
entry_name = f"{vm['name_label']}_{vm_duplicate_count}"
vm_name_list.append(vm['name_label'])
vm_name_list.append(vm["name_label"])
else:
entry_name = uuid
group = 'with_ip'
ip = vm.get('mainIpAddress')
power_state = vm['power_state'].lower()
pool_name = self._pool_group_name_for_uuid(pools, vm['$poolId'])
host_name = self._host_group_name_for_uuid(hosts, vm['$container'])
group = "with_ip"
ip = vm.get("mainIpAddress")
power_state = vm["power_state"].lower()
pool_name = self._pool_group_name_for_uuid(pools, vm["$poolId"])
host_name = self._host_group_name_for_uuid(hosts, vm["$container"])
self.inventory.add_host(entry_name)
@ -248,67 +239,58 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# Grouping VMs with an IP together
if ip is None:
group = 'without_ip'
group = "without_ip"
self.inventory.add_group(group)
self.inventory.add_child(group, entry_name)
# Adding meta
self.inventory.set_variable(entry_name, 'uuid', uuid)
self.inventory.set_variable(entry_name, 'ip', ip)
self.inventory.set_variable(entry_name, 'ansible_host', ip)
self.inventory.set_variable(entry_name, 'power_state', power_state)
self.inventory.set_variable(
entry_name, 'name_label', vm['name_label'])
self.inventory.set_variable(entry_name, 'type', vm['type'])
self.inventory.set_variable(
entry_name, 'cpus', vm['CPUs']['number'])
self.inventory.set_variable(entry_name, 'tags', vm['tags'])
self.inventory.set_variable(
entry_name, 'memory', vm['memory']['size'])
self.inventory.set_variable(
entry_name, 'has_ip', group == 'with_ip')
self.inventory.set_variable(
entry_name, 'is_managed', vm.get('managementAgentDetected', False))
self.inventory.set_variable(
entry_name, 'os_version', vm['os_version'])
self.inventory.set_variable(entry_name, "uuid", uuid)
self.inventory.set_variable(entry_name, "ip", ip)
self.inventory.set_variable(entry_name, "ansible_host", ip)
self.inventory.set_variable(entry_name, "power_state", power_state)
self.inventory.set_variable(entry_name, "name_label", vm["name_label"])
self.inventory.set_variable(entry_name, "type", vm["type"])
self.inventory.set_variable(entry_name, "cpus", vm["CPUs"]["number"])
self.inventory.set_variable(entry_name, "tags", vm["tags"])
self.inventory.set_variable(entry_name, "memory", vm["memory"]["size"])
self.inventory.set_variable(entry_name, "has_ip", group == "with_ip")
self.inventory.set_variable(entry_name, "is_managed", vm.get("managementAgentDetected", False))
self.inventory.set_variable(entry_name, "os_version", vm["os_version"])
self._apply_constructable(entry_name, self.inventory.get_host(entry_name).get_vars())
def _add_hosts(self, hosts, pools):
host_name_list = []
for host in hosts.values():
if self.host_entry_name_type == 'name_label':
if host['name_label'] not in host_name_list:
entry_name = host['name_label']
host_name_list.append(host['name_label'])
if self.host_entry_name_type == "name_label":
if host["name_label"] not in host_name_list:
entry_name = host["name_label"]
host_name_list.append(host["name_label"])
else:
host_duplicate_count = host_name_list.count(host['name_label'])
host_duplicate_count = host_name_list.count(host["name_label"])
entry_name = f"{host['name_label']}_{host_duplicate_count}"
host_name_list.append(host['name_label'])
host_name_list.append(host["name_label"])
else:
entry_name = host['uuid']
entry_name = host["uuid"]
group_name = f"xo_host_{clean_group_name(host['name_label'])}"
pool_name = self._pool_group_name_for_uuid(pools, host['$poolId'])
pool_name = self._pool_group_name_for_uuid(pools, host["$poolId"])
self.inventory.add_group(group_name)
self.inventory.add_host(entry_name)
self.inventory.add_child(HOST_GROUP, entry_name)
self.inventory.add_child(pool_name, entry_name)
self.inventory.set_variable(entry_name, 'enabled', host['enabled'])
self.inventory.set_variable(
entry_name, 'hostname', host['hostname'])
self.inventory.set_variable(entry_name, 'memory', host['memory'])
self.inventory.set_variable(entry_name, 'address', host['address'])
self.inventory.set_variable(entry_name, 'cpus', host['cpus'])
self.inventory.set_variable(entry_name, 'type', 'host')
self.inventory.set_variable(entry_name, 'tags', host['tags'])
self.inventory.set_variable(entry_name, 'version', host['version'])
self.inventory.set_variable(
entry_name, 'power_state', host['power_state'].lower())
self.inventory.set_variable(
entry_name, 'product_brand', host['productBrand'])
self.inventory.set_variable(entry_name, "enabled", host["enabled"])
self.inventory.set_variable(entry_name, "hostname", host["hostname"])
self.inventory.set_variable(entry_name, "memory", host["memory"])
self.inventory.set_variable(entry_name, "address", host["address"])
self.inventory.set_variable(entry_name, "cpus", host["cpus"])
self.inventory.set_variable(entry_name, "type", "host")
self.inventory.set_variable(entry_name, "tags", host["tags"])
self.inventory.set_variable(entry_name, "version", host["version"])
self.inventory.set_variable(entry_name, "power_state", host["power_state"].lower())
self.inventory.set_variable(entry_name, "product_brand", host["productBrand"])
for pool in pools.values():
group_name = f"xo_pool_{clean_group_name(pool['name_label'])}"
@ -340,25 +322,27 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
for group in POWER_STATES:
self.inventory.add_group(group.lower())
self._add_pools(objects['pools'])
self._add_hosts(objects['hosts'], objects['pools'])
self._add_vms(objects['vms'], objects['hosts'], objects['pools'])
self._add_pools(objects["pools"])
self._add_hosts(objects["hosts"], objects["pools"])
self._add_vms(objects["vms"], objects["hosts"], objects["pools"])
def verify_file(self, path):
valid = False
if super().verify_file(path):
if path.endswith(('xen_orchestra.yaml', 'xen_orchestra.yml')):
if path.endswith(("xen_orchestra.yaml", "xen_orchestra.yml")):
valid = True
else:
self.display.vvv(
'Skipping due to inventory source not ending in "xen_orchestra.yaml" nor "xen_orchestra.yml"')
'Skipping due to inventory source not ending in "xen_orchestra.yaml" nor "xen_orchestra.yml"'
)
return valid
def parse(self, inventory, loader, path, cache=True):
if not HAS_WEBSOCKET:
raise AnsibleError('This plugin requires websocket-client 1.0.0 or higher: '
'https://github.com/websocket-client/websocket-client.')
raise AnsibleError(
"This plugin requires websocket-client 1.0.0 or higher: "
"https://github.com/websocket-client/websocket-client."
)
super().parse(inventory, loader, path)
@ -366,24 +350,24 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
self._read_config_data(path)
self.inventory = inventory
self.protocol = 'wss'
self.xoa_api_host = self.get_option('api_host')
self.xoa_user = self.get_option('user')
self.xoa_password = self.get_option('password')
self.protocol = "wss"
self.xoa_api_host = self.get_option("api_host")
self.xoa_user = self.get_option("user")
self.xoa_password = self.get_option("password")
self.cache_key = self.get_cache_key(path)
self.use_cache = cache and self.get_option('cache')
self.use_cache = cache and self.get_option("cache")
self.validate_certs = self.get_option('validate_certs')
if not self.get_option('use_ssl'):
self.protocol = 'ws'
self.validate_certs = self.get_option("validate_certs")
if not self.get_option("use_ssl"):
self.protocol = "ws"
self.vm_entry_name_type = 'uuid'
if not self.get_option('use_vm_uuid'):
self.vm_entry_name_type = 'name_label'
self.vm_entry_name_type = "uuid"
if not self.get_option("use_vm_uuid"):
self.vm_entry_name_type = "name_label"
self.host_entry_name_type = 'uuid'
if not self.get_option('use_host_uuid'):
self.host_entry_name_type = 'name_label'
self.host_entry_name_type = "uuid"
if not self.get_option("use_host_uuid"):
self.host_entry_name_type = "name_label"
objects = self._get_objects()
self._populate(make_unsafe(objects))