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:
parent
3f2213791a
commit
340ff8586d
1008 changed files with 61301 additions and 58309 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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} ")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue