Skip to content

Commit

Permalink
add vm inventory plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mikemorency committed Dec 30, 2024
1 parent 77e9060 commit a866639
Show file tree
Hide file tree
Showing 8 changed files with 523 additions and 123 deletions.
3 changes: 3 additions & 0 deletions hosts.vms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugin: vmware.vmware.vms
search_paths:
- /Eco-Datacenter/vm/yblum
133 changes: 10 additions & 123 deletions plugins/inventory/esxi_hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,71 +150,28 @@

from ansible.errors import AnsibleError
from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

from ansible_collections.vmware.vmware.plugins.inventory_utils._base import VmwareInventoryBase
from ansible_collections.vmware.vmware.plugins.module_utils._vmware_folder_paths import (
get_folder_path_of_vsphere_object
)
from ansible_collections.vmware.vmware.plugins.module_utils._vmware_facts import (
vmware_obj_to_json,
flatten_dict
from ansible_collections.vmware.vmware.plugins.inventory_utils._base import (
VmwareInventoryBase,
VmwareInventoryHost
)


class EsxiInventoryHost():
class EsxiInventoryHost(VmwareInventoryHost):
def __init__(self):
self.object = None
self.inventory_hostname = None
self.path = ''
self.properties = dict()
super().__init__()
self._management_ip = None

@classmethod
def create_from_cache(cls, inventory_hostname, host_properties):
"""
Create the class from the inventory cache. We don't want to refresh the data or make any calls to vCenter.
Properties are populated from whatever we had previously cached.
"""
host = cls()
host.inventory_hostname = inventory_hostname
host.properties = host_properties
return host

@classmethod
def create_from_object(cls, host_object, properties_to_gather, pyvmomi_client):
def create_from_object(cls, vmware_object, properties_to_gather, pyvmomi_client):
"""
Create the class from a host object that we got from pyvmomi. The host properties will be populated
from the object and additional calls to vCenter
"""
host = cls()
host.object = host_object
host.path = get_folder_path_of_vsphere_object(host_object)
host.properties = host._set_properties_from_pyvmomi(properties_to_gather, pyvmomi_client)
host = super().create_from_object(vmware_object, properties_to_gather, pyvmomi_client)
host.properties['management_ip'] = host.management_ip
return host

def _set_properties_from_pyvmomi(self, properties_to_gather, pyvmomi_client):
properties = vmware_obj_to_json(self.object, properties_to_gather)
properties['path'] = self.path
properties['management_ip'] = self.management_ip

# Custom values
if hasattr(self.object, "customValue"):
properties['customValue'] = dict()
field_mgr = pyvmomi_client.custom_field_mgr
for cust_value in self.object.customValue:
properties['customValue'][
[y.name for y in field_mgr if y.key == cust_value.key][0]
] = cust_value.value

return properties

def sanitize_properties(self):
self.properties = camel_dict_to_snake_dict(self.properties)

def flatten_properties(self):
self.properties = flatten_dict(self.properties)

@property
def management_ip(self):
# We already looked up the management IP from vcenter this session, so
Expand Down Expand Up @@ -313,7 +270,7 @@ def populate_from_cache(self, cache_data):
for inventory_hostname, host_properties in cache_data.items():
esxi_host = EsxiInventoryHost.create_from_cache(
inventory_hostname=inventory_hostname,
host_properties=host_properties
properties=host_properties
)
self.__update_inventory(esxi_host)

Expand All @@ -332,7 +289,7 @@ def populate_from_vcenter(self, config_data):
continue

esxi_host = EsxiInventoryHost.create_from_object(
host_object=host_object,
vmware_object=host_object,
properties_to_gather=properties_to_gather,
pyvmomi_client=self.pyvmomi_client
)
Expand All @@ -354,35 +311,6 @@ def __update_inventory(self, esxi_host):
self.add_host_to_groups_based_on_path(esxi_host)
self.set_host_variables_from_host_properties(esxi_host)

def set_inventory_hostname(self, esxi_host):
"""
The user can specify a list of jinja templates, and the first valid template should be used for the
host's inventory hostname. The inventory hostname is mostly for decorative purposes since the
ansible_host value takes precedence when trying to connect.
"""
hostname = None
errors = []

for hostname_pattern in self.get_option("hostnames"):
try:
hostname = self._compose(template=hostname_pattern, variables=esxi_host.properties)
except Exception as e:
if self.get_option("strict"):
raise AnsibleError(
"Could not compose %s as hostnames - %s"
% (hostname_pattern, to_native(e))
)

errors.append((hostname_pattern, str(e)))
if hostname:
esxi_host.inventory_hostname = hostname
return

raise AnsibleError(
"Could not template any hostname for host, errors for each preference: %s"
% (", ".join(["%s: %s" % (pref, err) for pref, err in errors]))
)

def add_host_to_inventory(self, esxi_host: EsxiInventoryHost):
"""
Add the host to the inventory and any groups that the user wants to create based on inventory
Expand All @@ -398,44 +326,3 @@ def add_host_to_inventory(self, esxi_host: EsxiInventoryHost):
self.get_option("groups"), esxi_host.properties, esxi_host.inventory_hostname, strict=strict)
self._add_host_to_keyed_groups(
self.get_option("keyed_groups"), esxi_host.properties, esxi_host.inventory_hostname, strict=strict)

def add_host_to_groups_based_on_path(self, esxi_host: EsxiInventoryHost):
"""
If the user desires, create groups based on each ESXi host's path. A group is created for each
step down in the path, with the group from the step above containing subsequent groups.
Optionally, the user can add a prefix to the groups created by this process.
The final group in the path will be where the ESXi host is added.
"""
if not self.get_option("group_by_paths"):
return

path_parts = esxi_host.path.split('/')
group_name_parts = []
last_created_group = None

if self.get_option("group_by_paths_prefix"):
group_name_parts = [self.get_option("group_by_paths_prefix")]

for path_part in path_parts:
if not path_part:
continue
group_name_parts.append(path_part)
group_name = self._sanitize_group_name('_'.join(group_name_parts))
group = self.inventory.add_group(group_name)

if last_created_group:
self.inventory.add_child(last_created_group, group)
last_created_group = group

if last_created_group:
self.inventory.add_host(esxi_host.inventory_hostname, last_created_group)

def set_host_variables_from_host_properties(self, esxi_host):
if self.get_option("sanitize_property_names"):
esxi_host.sanitize_properties()

if self.get_option("flatten_nested_properties"):
esxi_host.flatten_properties()

for k, v in esxi_host.properties.items():
self.inventory.set_variable(esxi_host.inventory_hostname, k, v)
Loading

0 comments on commit a866639

Please sign in to comment.