Use hostnames in inventory

This patch updates the inventory code to produce an inventory that uses
hostnames as the host alias. Since the hostname may not always be
resolvable, ansible_host is added as a hostvar and set to the host's IP
address.

Using hostnames produces a much more user friendly result in the ansible
output showing task result and play recap.

The anti-pattern of creating one group per host is also removed in favor
of just adding additional hosts under the role named groups. E.g., the
"Controller" group will now contain 3 hosts when deploying 3 controllers
instead of having 3 child groups, where each child group only had 1
host.

hostvars are again used to set specific values on each host in the role
named group.

Change-Id: If834e5e60f79a7418cbc1441d2aaf85507ac7bd2
Closes-Bug: #1752057
This commit is contained in:
James Slagle 2018-04-03 12:09:22 -04:00
parent 60f25d2bcd
commit 5822ccc620
4 changed files with 212 additions and 170 deletions

View File

@ -0,0 +1,6 @@
---
other:
- The inventory code is updated to use hostnames as the host alias. Since the
hostname may not always be resolvable, ansible_host is added as a hostvar
and set to the host's IP address. Using hostnames produces a much more user
friendly result in the ansible output showing task result and play recap.

View File

@ -151,8 +151,10 @@ class TripleoInventory(object):
def list(self):
ret = OrderedDict({
'undercloud': {
'hosts': self._hosts(['localhost']),
'Undercloud': {
'hosts': {
'undercloud': {
'ansible_host': 'localhost'}},
'vars': {
'ansible_connection': 'local',
'auth_url': self.auth_url,
@ -170,25 +172,25 @@ class TripleoInventory(object):
if self.session:
swift_url = self.session.get_endpoint(service_type='object-store',
interface='public')
ret['undercloud']['vars']['undercloud_swift_url'] = swift_url
ret['Undercloud']['vars']['undercloud_swift_url'] = swift_url
keystone_url = self.stack_outputs.get('KeystoneURL')
if keystone_url:
ret['undercloud']['vars']['overcloud_keystone_url'] = keystone_url
ret['Undercloud']['vars']['overcloud_keystone_url'] = keystone_url
admin_password = self.get_overcloud_environment().get(
'parameter_defaults', {}).get('AdminPassword')
if admin_password:
ret['undercloud']['vars']['overcloud_admin_password'] =\
ret['Undercloud']['vars']['overcloud_admin_password'] =\
admin_password
endpoint_map = self.stack_outputs.get('EndpointMap')
ret['undercloud']['vars']['undercloud_service_list'] = \
ret['Undercloud']['vars']['undercloud_service_list'] = \
self.get_undercloud_service_list()
if endpoint_map:
horizon_endpoint = endpoint_map.get('HorizonPublic', {}).get('uri')
if horizon_endpoint:
ret['undercloud']['vars']['overcloud_horizon_url'] =\
ret['Undercloud']['vars']['overcloud_horizon_url'] =\
horizon_endpoint
role_net_ip_map = self.stack_outputs.get('RoleNetIpMap', {})
@ -201,26 +203,30 @@ class TripleoInventory(object):
if hostnames:
names = hostnames.get(HOST_NETWORK) or []
shortnames = [n.split(".%s." % HOST_NETWORK)[0] for n in names]
# Create a group per hostname to map hostname to IP
ips = role_net_ip_map[role][HOST_NETWORK]
hosts = {}
for idx, name in enumerate(shortnames):
ret[name] = {'hosts': self._hosts([ips[idx]])}
hosts[name] = {}
hosts[name].update({
'ansible_host': ips[idx]})
if 'server_ids' in role_node_id_map:
ret[name]['vars'] = {
hosts[name].update({
'deploy_server_id': role_node_id_map[
'server_ids'][role][idx]}
'server_ids'][role][idx]})
# Add variable for listing enabled networks in the node
ret[name]['vars']['enabled_networks'] = \
[str(net) for net in role_net_ip_map[role]]
hosts[name].update({
'enabled_networks':
[str(net) for net in role_net_ip_map[role]]})
# Add variable for IP on each network
for net in role_net_ip_map[role]:
ret[name]['vars']["%s_ip" % net] = \
role_net_ip_map[role][net][idx]
networks.update(ret[name]['vars']['enabled_networks'])
hosts[name].update({
"%s_ip" % net:
role_net_ip_map[role][net][idx]})
networks.update(hosts[name]['enabled_networks'])
children.append(role)
ret[role] = {
'children': self._hosts(sorted(shortnames)),
'hosts': hosts,
'vars': {
'ansible_ssh_user': self.ansible_ssh_user,
'bootstrap_server_id': role_node_id_map.get(

View File

@ -186,65 +186,75 @@ class TestInventory(base.TestCase):
self._inventory_list(inventory)
def _inventory_list(self, inventory):
expected = {'c-0': {'hosts': ['x.x.x.1'],
'vars': {'deploy_server_id': 'a',
'ctlplane_ip': 'x.x.x.1',
'enabled_networks': ['ctlplane']}},
'c-1': {'hosts': ['x.x.x.2'],
'vars': {'deploy_server_id': 'b',
'ctlplane_ip': 'x.x.x.2',
'enabled_networks': ['ctlplane']}},
'c-2': {'hosts': ['x.x.x.3'],
'vars': {'deploy_server_id': 'c',
'ctlplane_ip': 'x.x.x.3',
'enabled_networks': ['ctlplane']}},
'Compute': {
'children': ['cp-0'],
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {
'children': ['c-0', 'c-1', 'c-2'],
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'cp-0': {'hosts': ['y.y.y.1'],
'vars': {'deploy_server_id': 'd',
'ctlplane_ip': 'y.y.y.1',
'enabled_networks': ['ctlplane']}},
'cs-0': {'hosts': ['z.z.z.1'],
'vars': {'deploy_server_id': 'e',
'ctlplane_ip': 'z.z.z.1',
'enabled_networks': ['ctlplane']}},
'CustomRole': {
'children': ['cs-0'],
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'overcloud': {
'children': ['Compute', 'Controller', 'CustomRole'],
'vars': {
'ctlplane_vip': 'x.x.x.4',
'redis_vip': 'x.x.x.6'}},
ansible_ssh_user = 'heat-admin'
expected = {
'Compute': {
'hosts': {
'cp-0': {
'ansible_host': 'y.y.y.1',
'ctlplane_ip': 'y.y.y.1',
'deploy_server_id': 'd',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {
'hosts': {
'c-0': {
'ansible_host': 'x.x.x.1',
'ctlplane_ip': 'x.x.x.1',
'deploy_server_id': 'a',
'enabled_networks': ['ctlplane']},
'c-1': {
'ansible_host': 'x.x.x.2',
'ctlplane_ip': 'x.x.x.2',
'deploy_server_id': 'b',
'enabled_networks': ['ctlplane']},
'c-2': {
'ansible_host': 'x.x.x.3',
'ctlplane_ip': 'x.x.x.3',
'deploy_server_id': 'c',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'CustomRole': {
'hosts': {
'cs-0': {
'ansible_host': 'z.z.z.1',
'ctlplane_ip': 'z.z.z.1',
'deploy_server_id': 'e',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'overcloud': {
'children': ['Compute', 'Controller', 'CustomRole'],
'vars': {
'ctlplane_vip': 'x.x.x.4',
'redis_vip': 'x.x.x.6'}},
'Undercloud': {
'hosts': {
'undercloud': {
'hosts': ['localhost'],
'vars': {'ansible_connection': 'local',
'auth_url': 'xyz://keystone.local',
'cacert': 'acacert',
'os_auth_token': 'atoken',
'overcloud_keystone_url': 'xyz://keystone',
'overcloud_admin_password': 'theadminpw',
'plan': 'overcloud',
'project_name': 'admin',
'undercloud_service_list': [
'openstack-nova-compute',
'openstack-heat-engine',
'openstack-ironic-conductor',
'openstack-swift-container',
'openstack-swift-object',
'openstack-mistral-engine'],
'undercloud_swift_url': 'anendpoint',
'username': 'admin'}}}
'ansible_host': 'localhost'}},
'vars': {'ansible_connection': 'local',
'auth_url': 'xyz://keystone.local',
'cacert': 'acacert',
'os_auth_token': 'atoken',
'overcloud_keystone_url': 'xyz://keystone',
'overcloud_admin_password': 'theadminpw',
'plan': 'overcloud',
'project_name': 'admin',
'undercloud_service_list': [
'openstack-nova-compute',
'openstack-heat-engine',
'openstack-ironic-conductor',
'openstack-swift-container',
'openstack-swift-object',
'openstack-mistral-engine'],
'undercloud_swift_url': 'anendpoint',
'username': 'admin'}}}
inv_list = inventory.list()
for k in expected:
self.assertEqual(expected[k], inv_list[k])
@ -272,67 +282,75 @@ class TestInventory(base.TestCase):
self.inventory.stack_outputs = self.outputs
expected = {'c-0': {'hosts': ['x.x.x.1'],
'vars': {'deploy_server_id': 'a',
'ctlplane_ip': 'x.x.x.1',
'enabled_networks': ['ctlplane']}},
'c-1': {'hosts': ['x.x.x.2'],
'vars': {'deploy_server_id': 'b',
'ctlplane_ip': 'x.x.x.2',
'enabled_networks': ['ctlplane']}},
'c-2': {'hosts': ['x.x.x.3'],
'vars': {'deploy_server_id': 'c',
'ctlplane_ip': 'x.x.x.3',
'enabled_networks': ['ctlplane']}},
'Compute': {
'children': ['cp-0'],
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {
'children': ['c-0', 'c-1', 'c-2'],
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'cp-0': {'hosts': ['y.y.y.1'],
'vars': {'deploy_server_id': 'd',
'ctlplane_ip': 'y.y.y.1',
'enabled_networks': ['ctlplane']}},
'cs-0': {'hosts': ['z.z.z.1'],
'vars': {'deploy_server_id': 'e',
'ctlplane_ip': 'z.z.z.1',
'enabled_networks': ['ctlplane']}},
'CustomRole': {
'children': ['cs-0'],
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'overcloud': {
'children': ['Compute', 'Controller', 'CustomRole'],
'vars': {
'ctlplane_vip': 'x.x.x.4',
'redis_vip': 'x.x.x.6'}},
expected = {
'Compute': {
'hosts': {
'cp-0': {
'ansible_host': 'y.y.y.1',
'ctlplane_ip': 'y.y.y.1',
'deploy_server_id': 'd',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {
'hosts': {
'c-0': {
'ansible_host': 'x.x.x.1',
'ctlplane_ip': 'x.x.x.1',
'deploy_server_id': 'a',
'enabled_networks': ['ctlplane']},
'c-1': {
'ansible_host': 'x.x.x.2',
'ctlplane_ip': 'x.x.x.2',
'deploy_server_id': 'b',
'enabled_networks': ['ctlplane']},
'c-2': {
'ansible_host': 'x.x.x.3',
'ctlplane_ip': 'x.x.x.3',
'deploy_server_id': 'c',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'CustomRole': {
'hosts': {
'cs-0': {
'ansible_host': 'z.z.z.1',
'ctlplane_ip': 'z.z.z.1',
'deploy_server_id': 'e',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'overcloud': {
'children': ['Compute', 'Controller', 'CustomRole'],
'vars': {
'ctlplane_vip': 'x.x.x.4',
'redis_vip': 'x.x.x.6'}},
'Undercloud': {
'hosts': {
'undercloud': {
'hosts': ['localhost'],
'vars': {'ansible_connection': 'local',
'auth_url': 'xyz://keystone.local',
'cacert': 'acacert',
'os_auth_token':
'atoken' if session else None,
'overcloud_keystone_url': 'xyz://keystone',
'overcloud_admin_password': 'theadminpw',
'plan': 'overcloud',
'project_name': 'admin',
'undercloud_service_list': [
'openstack-nova-compute',
'openstack-heat-engine',
'openstack-ironic-conductor',
'openstack-swift-container',
'openstack-swift-object',
'openstack-mistral-engine'],
'undercloud_swift_url':
'anendpoint' if session else None,
'username': 'admin'}}}
'ansible_host': 'localhost'}},
'vars': {'ansible_connection': 'local',
'auth_url': 'xyz://keystone.local',
'cacert': 'acacert',
'os_auth_token':
'atoken' if session else None,
'overcloud_keystone_url': 'xyz://keystone',
'overcloud_admin_password': 'theadminpw',
'plan': 'overcloud',
'project_name': 'admin',
'undercloud_service_list': [
'openstack-nova-compute',
'openstack-heat-engine',
'openstack-ironic-conductor',
'openstack-swift-container',
'openstack-swift-object',
'openstack-mistral-engine'],
'undercloud_swift_url':
'anendpoint' if session else None,
'username': 'admin'}}}
inv_list = self.inventory.list()
for k in expected:
@ -342,47 +360,56 @@ class TestInventory(base.TestCase):
self._inventory_write_static()
def test_inventory_write_static_extra_vars(self):
extra_vars = {'undercloud': {'anextravar': 123}}
extra_vars = {'Undercloud': {'anextravar': 123}}
self._inventory_write_static(extra_vars=extra_vars)
def _inventory_write_static(self, extra_vars=None):
tmp_dir = self.useFixture(fixtures.TempDir()).path
inv_path = os.path.join(tmp_dir, "inventory.yaml")
self.inventory.write_static_inventory(inv_path, extra_vars)
ansible_ssh_user = 'heat-admin'
expected = {
'Compute': {'children': {'cp-0': {}},
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {'children': {'c-0': {}, 'c-1': {}, 'c-2': {}},
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'CustomRole': {'children': {'cs-0': {}},
'vars': {'ansible_ssh_user': 'heat-admin',
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'Compute': {
'hosts': {
'cp-0': {
'ansible_host': 'y.y.y.1',
'ctlplane_ip': 'y.y.y.1',
'deploy_server_id': 'd',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Compute'}},
'Controller': {
'hosts': {
'c-0': {
'ansible_host': 'x.x.x.1',
'ctlplane_ip': 'x.x.x.1',
'deploy_server_id': 'a',
'enabled_networks': ['ctlplane']},
'c-1': {
'ansible_host': 'x.x.x.2',
'ctlplane_ip': 'x.x.x.2',
'deploy_server_id': 'b',
'enabled_networks': ['ctlplane']},
'c-2': {
'ansible_host': 'x.x.x.3',
'ctlplane_ip': 'x.x.x.3',
'deploy_server_id': 'c',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'Controller'}},
'CustomRole': {
'hosts': {
'cs-0': {
'ansible_host': 'z.z.z.1',
'ctlplane_ip': 'z.z.z.1',
'deploy_server_id': 'e',
'enabled_networks': ['ctlplane']}},
'vars': {'ansible_ssh_user': ansible_ssh_user,
'bootstrap_server_id': 'a',
'role_name': 'CustomRole'}},
'_meta': {'hostvars': {}},
'c-0': {'hosts': {'x.x.x.1': {}},
'vars': {'ctlplane_ip': 'x.x.x.1',
'deploy_server_id': 'a',
'enabled_networks': ['ctlplane']}},
'c-1': {'hosts': {'x.x.x.2': {}},
'vars': {'ctlplane_ip': 'x.x.x.2',
'deploy_server_id': 'b',
'enabled_networks': ['ctlplane']}},
'c-2': {'hosts': {'x.x.x.3': {}},
'vars': {'ctlplane_ip': 'x.x.x.3',
'deploy_server_id': 'c',
'enabled_networks': ['ctlplane']}},
'cp-0': {'hosts': {'y.y.y.1': {}},
'vars': {'ctlplane_ip': 'y.y.y.1',
'deploy_server_id': 'd',
'enabled_networks': ['ctlplane']}},
'cs-0': {'hosts': {'z.z.z.1': {}},
'vars': {'ctlplane_ip': 'z.z.z.1',
'deploy_server_id': 'e',
'enabled_networks': ['ctlplane']}},
'overcloud': {'children': {'Compute': {},
'Controller': {},
'CustomRole': {}},
@ -400,7 +427,8 @@ class TestInventory(base.TestCase):
'vars': {'ansible_ssh_user': 'heat-admin'}},
'sh': {'children': {'CustomRole': {}},
'vars': {'ansible_ssh_user': 'heat-admin'}},
'undercloud': {'hosts': {'localhost': {}},
'Undercloud': {'hosts': {'undercloud': {
'ansible_host': 'localhost'}},
'vars': {'ansible_connection': 'local',
'auth_url': 'xyz://keystone.local',
'cacert': 'acacert',
@ -419,7 +447,7 @@ class TestInventory(base.TestCase):
'undercloud_swift_url': 'anendpoint',
'username': 'admin'}}}
if extra_vars:
expected['undercloud']['vars']['anextravar'] = 123
expected['Undercloud']['vars']['anextravar'] = 123
with open(inv_path, 'r') as f:
loaded_inv = yaml.safe_load(f)

View File

@ -272,6 +272,8 @@ class Config(object):
group_vars_dir = os.path.join(tmp_path, 'group_vars')
self._mkdir(group_vars_dir)
host_vars_dir = os.path.join(tmp_path, 'host_vars')
self._mkdir(host_vars_dir)
for server, deployments in server_deployments.items():
deployment_template = env.get_template('deployment.j2')