Add write_static_inventory to TripleoInventory class
This is a reimplementation of the similarly names function in tripleo-ansible-inventory, but this outputs yaml format instead of the current ini format. The reason for this change is getting consistent behavior of the configparser module for unit tests over py27 & py35 is difficult, and also some of the roles_data keys I plan to add in a subsequent commit can't easily be represented in the ini format (particularly lists don't seem to work, everything must be a string value). The tripleo-ansible-inventory script can be changed to use this interface, and we'll have to deprecate the current ini based interface. Change-Id: Ia3c6b9931e2d38507917cd80a93857d952784bd2
This commit is contained in:
parent
35e470776b
commit
513620b115
|
@ -16,12 +16,23 @@
|
|||
# under the License.
|
||||
|
||||
from collections import OrderedDict
|
||||
import os.path
|
||||
import yaml
|
||||
|
||||
from heatclient.exc import HTTPNotFound
|
||||
|
||||
HOST_NETWORK = 'ctlplane'
|
||||
|
||||
|
||||
class TemplateDumper(yaml.SafeDumper):
|
||||
def represent_ordered_dict(self, data):
|
||||
return self.represent_dict(data.items())
|
||||
|
||||
|
||||
TemplateDumper.add_representer(OrderedDict,
|
||||
TemplateDumper.represent_ordered_dict)
|
||||
|
||||
|
||||
class StackOutputs(object):
|
||||
"""Item getter for stack outputs.
|
||||
|
||||
|
@ -77,6 +88,7 @@ class TripleoInventory(object):
|
|||
cacert=None, username=None, ansible_ssh_user=None):
|
||||
self.session = session
|
||||
self.hclient = hclient
|
||||
self.hosts_format_dict = False
|
||||
if configs is not None:
|
||||
# FIXME(shardy) backwards compatibility until we switch
|
||||
# tripleo-validations to pass the individual values
|
||||
|
@ -130,10 +142,17 @@ class TripleoInventory(object):
|
|||
"""
|
||||
return self.UNDERCLOUD_SERVICES
|
||||
|
||||
def _hosts(self, alist):
|
||||
"""Static yaml inventories reqire a different hosts format?!"""
|
||||
if self.hosts_format_dict:
|
||||
return {x: {} for x in alist}
|
||||
else:
|
||||
return alist
|
||||
|
||||
def list(self):
|
||||
ret = OrderedDict({
|
||||
'undercloud': {
|
||||
'hosts': ['localhost'],
|
||||
'hosts': self._hosts(['localhost']),
|
||||
'vars': {
|
||||
'ansible_connection': 'local',
|
||||
'auth_url': self.auth_url,
|
||||
|
@ -185,7 +204,7 @@ class TripleoInventory(object):
|
|||
# Create a group per hostname to map hostname to IP
|
||||
ips = role_net_ip_map[role][HOST_NETWORK]
|
||||
for idx, name in enumerate(shortnames):
|
||||
ret[name] = {'hosts': [ips[idx]]}
|
||||
ret[name] = {'hosts': self._hosts([ips[idx]])}
|
||||
if 'server_ids' in role_node_id_map:
|
||||
ret[name]['vars'] = {
|
||||
'deploy_server_id': role_node_id_map[
|
||||
|
@ -201,7 +220,7 @@ class TripleoInventory(object):
|
|||
|
||||
children.append(role)
|
||||
ret[role] = {
|
||||
'children': sorted(shortnames),
|
||||
'children': self._hosts(sorted(shortnames)),
|
||||
'vars': {
|
||||
'ansible_ssh_user': self.ansible_ssh_user,
|
||||
'bootstrap_server_id': role_node_id_map.get(
|
||||
|
@ -216,7 +235,7 @@ class TripleoInventory(object):
|
|||
for vip_name, vip in vip_map.items()
|
||||
if vip and (vip_name in networks or vip_name == 'redis')}
|
||||
ret['overcloud'] = {
|
||||
'children': sorted(children),
|
||||
'children': self._hosts(sorted(children)),
|
||||
'vars': vips
|
||||
}
|
||||
|
||||
|
@ -228,7 +247,7 @@ class TripleoInventory(object):
|
|||
if ret.get(role) is not None]
|
||||
if service_children:
|
||||
ret[service.lower()] = {
|
||||
'children': service_children,
|
||||
'children': self._hosts(service_children),
|
||||
'vars': {
|
||||
'ansible_ssh_user': self.ansible_ssh_user
|
||||
}
|
||||
|
@ -245,3 +264,19 @@ class TripleoInventory(object):
|
|||
# provide detailed info for hosts:
|
||||
# http://docs.ansible.com/ansible/developing_inventory.html
|
||||
return {}
|
||||
|
||||
def write_static_inventory(self, inventory_file_path):
|
||||
"""Convert inventory list to static yaml format in a file."""
|
||||
allowed_extensions = ('.yaml', '.yml', '.json')
|
||||
if not os.path.splitext(inventory_file_path)[1] in allowed_extensions:
|
||||
raise ValueError("Path %s does not end with one of %s extensions"
|
||||
% (inventory_file_path,
|
||||
",".join(allowed_extensions)))
|
||||
|
||||
# For some reason the json/yaml format needed for static and
|
||||
# dynamic inventories is different for the hosts/children?!
|
||||
self.hosts_format_dict = True
|
||||
inventory = self.list()
|
||||
|
||||
with open(inventory_file_path, 'w') as inventory_file:
|
||||
yaml.dump(inventory, inventory_file, TemplateDumper)
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from heatclient.exc import HTTPNotFound
|
||||
from mock import MagicMock
|
||||
|
||||
|
@ -54,9 +58,9 @@ class TestInventory(base.TestCase):
|
|||
self.outputs_data = {'outputs': [
|
||||
{'output_key': 'EnabledServices',
|
||||
'output_value': {
|
||||
'Controller': ['a', 'b', 'c'],
|
||||
'Compute': ['d', 'e', 'f'],
|
||||
'CustomRole': ['g', 'h', 'i']}},
|
||||
'Controller': ['sa', 'sb'],
|
||||
'Compute': ['sd', 'se'],
|
||||
'CustomRole': ['sg', 'sh']}},
|
||||
{'output_key': 'KeystoneURL',
|
||||
'output_value': 'xyz://keystone'},
|
||||
{'output_key': 'ServerIdData',
|
||||
|
@ -327,3 +331,80 @@ class TestInventory(base.TestCase):
|
|||
inv_list = self.inventory.list()
|
||||
for k in expected:
|
||||
self.assertEqual(expected[k], inv_list[k])
|
||||
|
||||
def test_inventory_write_static(self):
|
||||
tmp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
inv_path = os.path.join(tmp_dir, "inventory.yaml")
|
||||
self.inventory.write_static_inventory(inv_path)
|
||||
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'}},
|
||||
'_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': {}},
|
||||
'vars': {'ctlplane_vip': 'x.x.x.4',
|
||||
'redis_vip': 'x.x.x.6'}},
|
||||
'sa': {'children': {'Controller': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'sb': {'children': {'Controller': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'sd': {'children': {'Compute': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'se': {'children': {'Compute': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'sg': {'children': {'CustomRole': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'sh': {'children': {'CustomRole': {}},
|
||||
'vars': {'ansible_ssh_user': 'heat-admin'}},
|
||||
'undercloud': {'hosts': {'localhost': {}},
|
||||
'vars': {'ansible_connection': 'local',
|
||||
'auth_url': 'xyz://keystone.local',
|
||||
'cacert': 'acacert',
|
||||
'os_auth_token': 'atoken',
|
||||
'overcloud_admin_password': 'theadminpw',
|
||||
'overcloud_keystone_url': 'xyz://keystone',
|
||||
'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'}}}
|
||||
with open(inv_path, 'r') as f:
|
||||
loaded_inv = yaml.safe_load(f)
|
||||
self.assertEqual(expected, loaded_inv)
|
||||
|
|
Loading…
Reference in New Issue