Introduce TripleoInventories object
This object can be constructed by passing it a mapping of stack name to TripleoInventory objects. It has a merge method which merges all of the TripleoInventory objects into one OrderedDict which appends the stack name and an underscore to each host group to avoid name collisions. It has a write_static_inventory method which writes the merged OrderedDict into an Ansible inventory. Change-Id: I522f1acbd39ad36ae1fec896188d3e20e0575566 Implements: blueprint split-control-plane-unified-inventory
This commit is contained in:
parent
0c494e1c05
commit
aeaa571765
105
tripleo_common/inventories.py
Normal file
105
tripleo_common/inventories.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from collections import OrderedDict
|
||||
import os.path
|
||||
import yaml
|
||||
|
||||
|
||||
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 TripleoInventories(object):
|
||||
def __init__(self, stack_to_inv_obj_map):
|
||||
"""
|
||||
Input: a mapping of stack->TripleoInventory objects, e.g.
|
||||
stack_to_inv_obj_map['central'] = TripleoInventory('central')
|
||||
stack_to_inv_obj_map['edge0'] = TripleoInventory('edge0')
|
||||
"""
|
||||
self.stack_to_inv_obj_map = stack_to_inv_obj_map
|
||||
self.inventory = OrderedDict()
|
||||
|
||||
def merge(self):
|
||||
"""Merge TripleoInventory objects into self.inventory"""
|
||||
for stack, inv_obj in self.stack_to_inv_obj_map.items():
|
||||
# convert each inventory object into an ordered dict
|
||||
inv = inv_obj.list()
|
||||
# only want one undercloud, shouldn't matter which
|
||||
if 'Undercloud' not in self.inventory.keys():
|
||||
self.inventory['Undercloud'] = inv['Undercloud']
|
||||
self.inventory['Undercloud']['hosts'] = {'undercloud': {}}
|
||||
# add 'plans' to create a list to append to
|
||||
self.inventory['Undercloud']['vars']['plans'] = []
|
||||
|
||||
# save the plan for this stack in the plans list
|
||||
plan = inv['Undercloud']['vars']['plan']
|
||||
self.inventory['Undercloud']['vars']['plans'].append(plan)
|
||||
|
||||
for key in inv.keys():
|
||||
if key != 'Undercloud':
|
||||
new_key = stack + '_' + key
|
||||
if 'children' in inv[key].keys():
|
||||
roles = []
|
||||
for child in inv[key]['children']:
|
||||
roles.append(stack + '_' + child)
|
||||
self.inventory[new_key] = {}
|
||||
self.inventory[new_key]['vars'] = inv[key]['vars']
|
||||
for role in roles:
|
||||
self.inventory[new_key]['children'] = {}
|
||||
self.inventory[new_key]['children'][role] = {}
|
||||
if key == 'overcloud':
|
||||
# useful to have just stack name refer to children
|
||||
self.inventory[stack] = self.inventory[new_key]
|
||||
else:
|
||||
if key != '_meta':
|
||||
self.inventory[new_key] = inv[key]
|
||||
self.inventory[new_key]['hosts'] = {}
|
||||
self.inventory[new_key]['hosts'] = \
|
||||
inv['_meta']['hostvars']
|
||||
|
||||
# 'plan' doesn't make sense when using multiple plans
|
||||
self.inventory['Undercloud']['vars']['plan'] = ''
|
||||
# sort plans list for consistency
|
||||
self.inventory['Undercloud']['vars']['plans'].sort()
|
||||
|
||||
def write_static_inventory(self, inventory_file_path, extra_vars=None):
|
||||
"""Convert OrderedDict inventory 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)))
|
||||
|
||||
if extra_vars:
|
||||
for var, value in extra_vars.items():
|
||||
if var in self.inventory:
|
||||
self.inventory[var]['vars'].update(value)
|
||||
|
||||
with open(inventory_file_path, 'w') as inventory_file:
|
||||
yaml.dump(self.inventory, inventory_file, TemplateDumper)
|
||||
|
||||
def host(self):
|
||||
# Dynamic inventory scripts must return empty json if they don't
|
||||
# provide detailed info for hosts:
|
||||
# http://docs.ansible.com/ansible/developing_inventory.html
|
||||
return {}
|
418
tripleo_common/tests/test_inventories.py
Normal file
418
tripleo_common/tests/test_inventories.py
Normal file
@ -0,0 +1,418 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from mock import MagicMock
|
||||
from tripleo_common.tests import base
|
||||
from tripleo_common.inventories import TripleoInventories
|
||||
|
||||
|
||||
class TestInventories(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestInventories, self).setUp()
|
||||
mock_inv_central = MagicMock()
|
||||
mock_inv_edge0 = MagicMock()
|
||||
mock_inv_central.list.return_value = self._mock_inv_central_data()
|
||||
mock_inv_edge0.list.return_value = self._mock_inv_edge0_data()
|
||||
self.stack_to_inv_obj_map = {
|
||||
'central': mock_inv_central,
|
||||
'edge0': mock_inv_edge0
|
||||
}
|
||||
self.inventories = TripleoInventories(self.stack_to_inv_obj_map)
|
||||
|
||||
def test_merge(self):
|
||||
self.inventories.merge()
|
||||
expected = self._mock_inv_merged_data()
|
||||
for k in expected.keys():
|
||||
self.assertEqual(expected[k], self.inventories.inventory[k])
|
||||
|
||||
def test_inventory_write_static(self):
|
||||
self.inventories.merge()
|
||||
tmp_dir = self.useFixture(fixtures.TempDir()).path
|
||||
inv_path = os.path.join(tmp_dir, "inventory.yaml")
|
||||
self.inventories.write_static_inventory(inv_path)
|
||||
expected = self._mock_inv_merged_data()
|
||||
with open(inv_path, 'r') as f:
|
||||
loaded_inv = yaml.safe_load(f)
|
||||
self.assertEqual(expected, loaded_inv)
|
||||
|
||||
def _mock_inv_central_data(self):
|
||||
return {
|
||||
"Undercloud": {
|
||||
"hosts": [
|
||||
"undercloud"
|
||||
],
|
||||
"vars": {
|
||||
"username": "admin",
|
||||
"overcloud_keystone_url": "http://192.168.24.21:5000",
|
||||
"project_name": "admin",
|
||||
"overcloud_horizon_url": "http://192.168.24.21/dashboard",
|
||||
"auth_url": "https://192.168.24.2:13000",
|
||||
"ansible_connection": "local",
|
||||
"cacert": "/etc/pki/ca-trust/cm-local-ca.pem",
|
||||
"ansible_host": "localhost",
|
||||
"ansible_remote_tmp": "/tmp/ansible-${USER}",
|
||||
"undercloud_service_list": [
|
||||
"tripleo_nova_compute",
|
||||
"tripleo_heat_engine",
|
||||
"tripleo_ironic_conductor",
|
||||
"tripleo_swift_container_server",
|
||||
"tripleo_swift_object_server",
|
||||
"tripleo_mistral_engine"
|
||||
],
|
||||
"ansible_python_interpreter": "/usr/bin/python",
|
||||
"overcloud_admin_password": "7uCCDn4lIKQ4i7ONsPdgX1KbC",
|
||||
"plan": "central"
|
||||
}
|
||||
},
|
||||
"Controller": {
|
||||
"hosts": [
|
||||
"central-controller-0"
|
||||
],
|
||||
"vars": {
|
||||
"tripleo_role_name": "Controller",
|
||||
"serial": "1",
|
||||
"ansible_ssh_user": "heat-admin",
|
||||
}
|
||||
},
|
||||
"overcloud": {
|
||||
"children": [
|
||||
"Controller"
|
||||
],
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.21",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.21",
|
||||
"redis_vip": "192.168.24.11",
|
||||
"internal_api_vip": "192.168.24.21",
|
||||
"external_vip": "192.168.24.21",
|
||||
"storage_vip": "192.168.24.21"
|
||||
}
|
||||
},
|
||||
"kernel": {
|
||||
"children": [
|
||||
"Controller"
|
||||
],
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
}
|
||||
},
|
||||
"ovn_controller": {
|
||||
"children": [
|
||||
"Controller"
|
||||
],
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
}
|
||||
},
|
||||
"_meta": {
|
||||
"hostvars": {
|
||||
"central-controller-0": {
|
||||
"storage_ip": "192.168.24.12",
|
||||
"storage_mgmt_ip": "192.168.24.12",
|
||||
"external_ip": "192.168.24.12",
|
||||
"ctlplane_ip": "192.168.24.12",
|
||||
"tenant_ip": "192.168.24.12",
|
||||
"internal_api_ip": "192.168.24.12",
|
||||
"management_ip": "192.168.24.12",
|
||||
"enabled_networks": [
|
||||
"management",
|
||||
"storage",
|
||||
"ctlplane",
|
||||
"external",
|
||||
"internal_api",
|
||||
"storage_mgmt",
|
||||
"tenant"
|
||||
],
|
||||
"ansible_host": "192.168.24.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def _mock_inv_edge0_data(self):
|
||||
return {
|
||||
"Undercloud": {
|
||||
"hosts": [
|
||||
"undercloud"
|
||||
],
|
||||
"vars": {
|
||||
"username": "admin",
|
||||
"overcloud_keystone_url": "http://192.168.24.21:5000",
|
||||
"project_name": "admin",
|
||||
"overcloud_horizon_url": "http://192.168.24.21/dashboard",
|
||||
"auth_url": "https://192.168.24.2:13000",
|
||||
"ansible_connection": "local",
|
||||
"cacert": "/etc/pki/ca-trust/cm-local-ca.pem",
|
||||
"ansible_host": "localhost",
|
||||
"ansible_remote_tmp": "/tmp/ansible-${USER}",
|
||||
"undercloud_service_list": [
|
||||
"tripleo_nova_compute",
|
||||
"tripleo_heat_engine",
|
||||
"tripleo_ironic_conductor",
|
||||
"tripleo_swift_container_server",
|
||||
"tripleo_swift_object_server",
|
||||
"tripleo_mistral_engine"
|
||||
],
|
||||
"ansible_python_interpreter": "/usr/bin/python",
|
||||
"overcloud_admin_password": "7uCCDn4lIKQ4i7ONsPdgX1KbC",
|
||||
"plan": "edge0"
|
||||
}
|
||||
},
|
||||
"DistributedComputeHCI": {
|
||||
"hosts": [
|
||||
"edge0-distributedcomputehci-0"
|
||||
],
|
||||
"vars": {
|
||||
"tripleo_role_name": "DistributedComputeHCI",
|
||||
"serial": "1",
|
||||
"ansible_ssh_user": "heat-admin",
|
||||
}
|
||||
},
|
||||
"overcloud": {
|
||||
"children": [
|
||||
"DistributedComputeHCI"
|
||||
],
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.20",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.20",
|
||||
"redis_vip": "192.168.24.24",
|
||||
"internal_api_vip": "192.168.24.20",
|
||||
"external_vip": "192.168.24.20",
|
||||
"storage_vip": "192.168.24.20"
|
||||
}
|
||||
},
|
||||
"kernel": {
|
||||
"children": [
|
||||
"DistributedComputeHCI"
|
||||
],
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
}
|
||||
},
|
||||
"ovn_controller": {
|
||||
"children": [
|
||||
"DistributedComputeHCI"
|
||||
],
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
}
|
||||
},
|
||||
"_meta": {
|
||||
"hostvars": {
|
||||
"edge0-distributedcomputehci-0": {
|
||||
"storage_ip": "192.168.24.13",
|
||||
"storage_mgmt_ip": "192.168.24.13",
|
||||
"external_ip": "192.168.24.13",
|
||||
"ctlplane_ip": "192.168.24.13",
|
||||
"tenant_ip": "192.168.24.13",
|
||||
"internal_api_ip": "192.168.24.13",
|
||||
"management_ip": "192.168.24.13",
|
||||
"enabled_networks": [
|
||||
"management",
|
||||
"storage",
|
||||
"ctlplane",
|
||||
"external",
|
||||
"internal_api",
|
||||
"storage_mgmt",
|
||||
"tenant"
|
||||
],
|
||||
"ansible_host": "192.168.24.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def _mock_inv_merged_data(self):
|
||||
return {
|
||||
"Undercloud": {
|
||||
"hosts": {
|
||||
"undercloud": {}
|
||||
},
|
||||
"vars": {
|
||||
"username": "admin",
|
||||
"overcloud_keystone_url": "http://192.168.24.21:5000",
|
||||
"project_name": "admin",
|
||||
"overcloud_horizon_url": "http://192.168.24.21/dashboard",
|
||||
"auth_url": "https://192.168.24.2:13000",
|
||||
"ansible_connection": "local",
|
||||
"cacert": "/etc/pki/ca-trust/cm-local-ca.pem",
|
||||
"ansible_host": "localhost",
|
||||
"ansible_remote_tmp": "/tmp/ansible-${USER}",
|
||||
"undercloud_service_list": [
|
||||
"tripleo_nova_compute",
|
||||
"tripleo_heat_engine",
|
||||
"tripleo_ironic_conductor",
|
||||
"tripleo_swift_container_server",
|
||||
"tripleo_swift_object_server",
|
||||
"tripleo_mistral_engine"
|
||||
],
|
||||
"ansible_python_interpreter": "/usr/bin/python",
|
||||
"overcloud_admin_password": "7uCCDn4lIKQ4i7ONsPdgX1KbC",
|
||||
"plan": '',
|
||||
"plans": [
|
||||
"central",
|
||||
"edge0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"central_Controller": {
|
||||
"hosts": {
|
||||
"central-controller-0": {
|
||||
"storage_ip": "192.168.24.12",
|
||||
"storage_mgmt_ip": "192.168.24.12",
|
||||
"external_ip": "192.168.24.12",
|
||||
"ctlplane_ip": "192.168.24.12",
|
||||
"tenant_ip": "192.168.24.12",
|
||||
"internal_api_ip": "192.168.24.12",
|
||||
"management_ip": "192.168.24.12",
|
||||
"enabled_networks": [
|
||||
"management",
|
||||
"storage",
|
||||
"ctlplane",
|
||||
"external",
|
||||
"internal_api",
|
||||
"storage_mgmt",
|
||||
"tenant"
|
||||
],
|
||||
"ansible_host": "192.168.24.12"
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"tripleo_role_name": "Controller",
|
||||
"serial": "1",
|
||||
"ansible_ssh_user": "heat-admin",
|
||||
}
|
||||
},
|
||||
"central_overcloud": {
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.21",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.21",
|
||||
"redis_vip": "192.168.24.11",
|
||||
"internal_api_vip": "192.168.24.21",
|
||||
"external_vip": "192.168.24.21",
|
||||
"storage_vip": "192.168.24.21"
|
||||
},
|
||||
"children": {
|
||||
"central_Controller": {}
|
||||
}
|
||||
},
|
||||
"central": {
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.21",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.21",
|
||||
"redis_vip": "192.168.24.11",
|
||||
"internal_api_vip": "192.168.24.21",
|
||||
"external_vip": "192.168.24.21",
|
||||
"storage_vip": "192.168.24.21"
|
||||
},
|
||||
"children": {
|
||||
"central_Controller": {}
|
||||
}
|
||||
},
|
||||
"central_kernel": {
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
},
|
||||
"children": {
|
||||
"central_Controller": {}
|
||||
}
|
||||
},
|
||||
"central_ovn_controller": {
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
},
|
||||
"children": {
|
||||
"central_Controller": {}
|
||||
}
|
||||
},
|
||||
"edge0_DistributedComputeHCI": {
|
||||
"hosts": {
|
||||
"edge0-distributedcomputehci-0": {
|
||||
"storage_ip": "192.168.24.13",
|
||||
"storage_mgmt_ip": "192.168.24.13",
|
||||
"external_ip": "192.168.24.13",
|
||||
"ctlplane_ip": "192.168.24.13",
|
||||
"tenant_ip": "192.168.24.13",
|
||||
"internal_api_ip": "192.168.24.13",
|
||||
"management_ip": "192.168.24.13",
|
||||
"enabled_networks": [
|
||||
"management",
|
||||
"storage",
|
||||
"ctlplane",
|
||||
"external",
|
||||
"internal_api",
|
||||
"storage_mgmt",
|
||||
"tenant"
|
||||
],
|
||||
"ansible_host": "192.168.24.13"
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"tripleo_role_name": "DistributedComputeHCI",
|
||||
"serial": "1",
|
||||
"ansible_ssh_user": "heat-admin",
|
||||
}
|
||||
},
|
||||
"edge0_overcloud": {
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.20",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.20",
|
||||
"redis_vip": "192.168.24.24",
|
||||
"internal_api_vip": "192.168.24.20",
|
||||
"external_vip": "192.168.24.20",
|
||||
"storage_vip": "192.168.24.20"
|
||||
},
|
||||
"children": {
|
||||
"edge0_DistributedComputeHCI": {}
|
||||
}
|
||||
},
|
||||
"edge0": {
|
||||
"vars": {
|
||||
"storage_mgmt_vip": "192.168.24.20",
|
||||
"container_cli": "podman",
|
||||
"ctlplane_vip": "192.168.24.20",
|
||||
"redis_vip": "192.168.24.24",
|
||||
"internal_api_vip": "192.168.24.20",
|
||||
"external_vip": "192.168.24.20",
|
||||
"storage_vip": "192.168.24.20"
|
||||
},
|
||||
"children": {
|
||||
"edge0_DistributedComputeHCI": {}
|
||||
}
|
||||
},
|
||||
"edge0_kernel": {
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
},
|
||||
"children": {
|
||||
"edge0_DistributedComputeHCI": {}
|
||||
}
|
||||
},
|
||||
"edge0_ovn_controller": {
|
||||
"vars": {
|
||||
"ansible_ssh_user": "heat-admin"
|
||||
},
|
||||
"children": {
|
||||
"edge0_DistributedComputeHCI": {}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user