Esxi inventory plugin (#6895)

* Add in ESXI plugin as a choice

* added in vmware esxi as an inventory source
* made a migration that may not be needed but will need to circle back

* black formatting

* linter fixes that I missed in the first commit, squash

* Update esxi to use_fqcn to true

* added use_fqcn on the esxi cred to true to correctly lay down
  collection name

* add fqcn true

* updated vmware esxi to use true for fqcn

* Update defaults and re-order migrations

* updated defaults to add correct env var to get empty
* re-ordered migrations to be in line with others

* Add condition to replace vmware_esxi cred

* replace direct name match with vmware cred since source supports old
  cred

* add skeleton test

* quick pass, needs more
* squash this

* Add tests for creating inventory ESXI source

* add test case to test creating an inventory with different cred type
  to source name

* update test and linting

* added correct cred return since esxi uses same cred

* assert on status code

* assert that we received a 204

* Added new folder for vmware_exsi and empty json file.

* Corrected the misspelling of folder name to 'esxi'

* fixed misspelling for `vmware_`

---------

Co-authored-by: Thanhnguyet Vo <thavo@redhat.com>
This commit is contained in:
Jake Jackson 2025-04-02 10:42:38 -04:00 committed by Hao Liu
parent 9d9c125e47
commit d35d7f62ec
No known key found for this signature in database
7 changed files with 110 additions and 4 deletions

View File

@ -14,7 +14,20 @@ __all__ = [
'STANDARD_INVENTORY_UPDATE_ENV',
]
CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'controller', 'insights', 'terraform', 'openshift_virtualization')
CLOUD_PROVIDERS = (
'azure_rm',
'ec2',
'gce',
'vmware',
'vmware_esxi',
'openstack',
'rhv',
'satellite6',
'controller',
'insights',
'terraform',
'openshift_virtualization',
)
PRIVILEGE_ESCALATION_METHODS = [
('sudo', _('Sudo')),
('su', _('Su')),

View File

@ -0,0 +1,61 @@
# Generated by Django 4.2.18 on 2025-02-27 20:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('main', '0197_add_opa_query_path')]
operations = [
migrations.AlterField(
model_name='inventorysource',
name='source',
field=models.CharField(
choices=[
('file', 'File, Directory or Script'),
('constructed', 'Template additional groups and hostvars at runtime'),
('scm', 'Sourced from a Project'),
('ec2', 'Amazon EC2'),
('gce', 'Google Compute Engine'),
('azure_rm', 'Microsoft Azure Resource Manager'),
('vmware', 'VMware vCenter'),
('vmware_esxi', 'VMware ESXi'),
('satellite6', 'Red Hat Satellite 6'),
('openstack', 'OpenStack'),
('rhv', 'Red Hat Virtualization'),
('controller', 'Red Hat Ansible Automation Platform'),
('insights', 'Red Hat Insights'),
('terraform', 'Terraform State'),
('openshift_virtualization', 'OpenShift Virtualization'),
],
default=None,
max_length=32,
),
),
migrations.AlterField(
model_name='inventoryupdate',
name='source',
field=models.CharField(
choices=[
('file', 'File, Directory or Script'),
('constructed', 'Template additional groups and hostvars at runtime'),
('scm', 'Sourced from a Project'),
('ec2', 'Amazon EC2'),
('gce', 'Google Compute Engine'),
('azure_rm', 'Microsoft Azure Resource Manager'),
('vmware', 'VMware vCenter'),
('vmware_esxi', 'VMware ESXi'),
('satellite6', 'Red Hat Satellite 6'),
('openstack', 'OpenStack'),
('rhv', 'Red Hat Virtualization'),
('controller', 'Red Hat Ansible Automation Platform'),
('insights', 'Red Hat Insights'),
('terraform', 'Terraform State'),
('openshift_virtualization', 'OpenShift Virtualization'),
],
default=None,
max_length=32,
),
),
]

View File

@ -928,6 +928,7 @@ class InventorySourceOptions(BaseModel):
('gce', _('Google Compute Engine')),
('azure_rm', _('Microsoft Azure Resource Manager')),
('vmware', _('VMware vCenter')),
('vmware_esxi', _('VMware ESXi')),
('satellite6', _('Red Hat Satellite 6')),
('openstack', _('OpenStack')),
('rhv', _('Red Hat Virtualization')),
@ -1048,7 +1049,9 @@ class InventorySourceOptions(BaseModel):
# If a credential was provided, it's important that it matches
# the actual inventory source being used (Amazon requires Amazon
# credentials; Rackspace requires Rackspace credentials; etc...)
if source.replace('ec2', 'aws') != cred.kind:
if source == 'vmware_esxi' and source.replace('vmware_esxi', 'vmware') != cred.kind:
return _('VMWARE inventory sources (such as %s) require credentials for the matching cloud service.') % source
if source == 'ec2' and source.replace('ec2', 'aws') != cred.kind:
return _('Cloud-based inventory sources (such as %s) require credentials for the matching cloud service.') % source
# Allow an EC2 source to omit the credential. If Tower is running on
# an EC2 instance with an IAM Role assigned, boto will use credentials
@ -1068,7 +1071,7 @@ class InventorySourceOptions(BaseModel):
credential = None
for cred in self.credentials.all():
if self.source in CLOUD_PROVIDERS:
if cred.kind == self.source.replace('ec2', 'aws'):
if cred.kind == self.source.replace('ec2', 'aws') or cred.kind == self.source.replace('vmware_esxi', 'vmware'):
credential = cred
break
else:
@ -1437,7 +1440,7 @@ class PluginFileInjector(object):
namespace = None
collection = None
collection_migration = '2.9' # Starting with this version, we use collections
use_fqcn = False # plugin: name versus plugin: namespace.collection.name
use_fqcn = False # plugin: name versus plugin: namespace.collection.name, set to True in each plugin as needed
# TODO: delete this method and update unit tests
@classmethod
@ -1568,6 +1571,14 @@ class vmware(PluginFileInjector):
collection = 'vmware'
class vmware_esxi(PluginFileInjector):
plugin_name = 'esxi_hosts'
base_injector = 'managed'
namespace = 'vmware'
collection = 'vmware'
use_fqcn = True
class openstack(PluginFileInjector):
plugin_name = 'openstack'
namespace = 'openstack'

View File

@ -0,0 +1 @@
{}

View File

@ -529,6 +529,19 @@ class TestInventorySourceCredential:
patch(url=inv_src.get_absolute_url(), data={'credential': aws_cred.pk}, expect=200, user=admin_user)
assert list(inv_src.credentials.values_list('id', flat=True)) == [aws_cred.pk]
def test_vmware_cred_create_esxi_source(self, inventory, admin_user, organization, post, get):
"""Test that a vmware esxi source can be added with a vmware credential"""
from awx.main.models.credential import Credential, CredentialType
vmware = CredentialType.defaults['vmware']()
vmware.save()
vmware_cred = Credential.objects.create(credential_type=vmware, name="bar", organization=organization)
inv_src = InventorySource.objects.create(inventory=inventory, name='foobar', source='vmware_esxi')
r = post(url=reverse('api:inventory_source_credentials_list', kwargs={'pk': inv_src.pk}), data={'id': vmware_cred.pk}, expect=204, user=admin_user)
g = get(inv_src.get_absolute_url(), admin_user)
assert r.status_code == 204
assert g.data['credential'] == vmware_cred.pk
@pytest.mark.django_db
class TestControlledBySCM:

View File

@ -48,6 +48,8 @@ def credential_kind(source):
"""Given the inventory source kind, return expected credential kind"""
if source == 'openshift_virtualization':
return 'kubernetes_bearer_token'
if source == 'vmware_esxi':
return 'vmware'
return source.replace('ec2', 'aws')

View File

@ -746,6 +746,11 @@ VMWARE_EXCLUDE_EMPTY_GROUPS = True
VMWARE_VALIDATE_CERTS = False
# -----------------
# -- VMware ESXi --
# -----------------
VMWARE_ESXI_EXCLUDE_EMPTY_GROUPS = True
# ---------------------------
# -- Google Compute Engine --
# ---------------------------