Add Ironic virt type

Adds support for the Ironic virt driver.

Change-Id: I72a7fb65395e4ceddc77a92bccc7b4563af0750a
This commit is contained in:
Gabriel Adrian Samfira 2020-08-19 14:28:36 +00:00
parent eeaee2a094
commit 7ff7001982
10 changed files with 153 additions and 2 deletions

View File

@ -0,0 +1 @@
nova_compute_hooks.py

View File

@ -341,6 +341,21 @@ class NovaComputeVirtContext(context.OSContextGenerator):
return ctxt
class IronicAPIContext(context.OSContextGenerator):
interfaces = ["ironic-api"]
def __call__(self):
ctxt = {}
for rid in relation_ids('ironic-api'):
for unit in related_units(rid):
is_ready = relation_get(
'ironic-api-ready', rid=rid, unit=unit)
if is_ready:
ctxt["ironic_api_ready"] = is_ready
return ctxt
return ctxt
def assert_libvirt_rbd_imagebackend_allowed():
os_rel = "Juno"
os_ver = get_os_version_package('nova-common')

View File

@ -364,6 +364,12 @@ def compute_changed():
import_keystone_ca_cert()
@hooks.hook('ironic-api-relation-changed')
@restart_on_change(restart_map())
def ironic_api_changed():
CONFIGS.write(NOVA_CONF)
@hooks.hook('ceph-access-relation-joined')
@hooks.hook('ceph-relation-joined')
@restart_on_change(restart_map())

View File

@ -96,6 +96,7 @@ from nova_compute_context import (
NovaComputeCephContext,
NeutronComputeContext,
InstanceConsoleContext,
IronicAPIContext,
CEPH_CONF,
ceph_config_file,
HostIPContext,
@ -170,6 +171,7 @@ LIBVIRTD_CONF = '/etc/libvirt/libvirtd.conf'
LIBVIRT_BIN = '/etc/default/libvirt-bin'
LIBVIRT_BIN_OVERRIDES = '/etc/init/libvirt-bin.override'
NOVA_CONF = '%s/nova.conf' % NOVA_CONF_DIR
NOVA_COMPUTE_CONF = '%s/nova-compute.conf' % NOVA_CONF_DIR
VENDORDATA_FILE = '%s/vendor_data.json' % NOVA_CONF_DIR
QEMU_KVM = '/etc/default/qemu-kvm'
NOVA_API_AA_PROFILE_PATH = ('/etc/apparmor.d/{}'.format(NOVA_API_AA_PROFILE))
@ -203,6 +205,7 @@ BASE_RESOURCE_MAP = {
context.OSConfigFlagContext(),
CloudComputeContext(),
LxdContext(),
IronicAPIContext(),
NovaComputeLibvirtContext(),
NovaComputeCephContext(),
context.SyslogContext(),
@ -292,6 +295,7 @@ VIRT_TYPES = {
'uml': ['nova-compute-uml'],
'lxc': ['nova-compute-lxc'],
'lxd': ['nova-compute-lxd'],
'ironic': ['nova-compute-ironic'],
}
@ -332,7 +336,8 @@ def resource_map():
hook execution.
'''
# TODO: Cache this on first call?
if config('virt-type').lower() == 'lxd':
virt_type = config('virt-type').lower()
if virt_type in ('lxd', 'ironic'):
resource_map = deepcopy(BASE_RESOURCE_MAP)
else:
resource_map = deepcopy(LIBVIRT_RESOURCE_MAP)
@ -363,6 +368,16 @@ def resource_map():
resource_map.pop(NOVA_API_AA_PROFILE_PATH)
resource_map.pop(NOVA_NETWORK_AA_PROFILE_PATH)
if virt_type == 'ironic':
# NOTE(gsamfira): OpenStack versions prior to Victoria do not have a
# dedicated nova-compute-ironic package which provides a suitable
# nova-compute.conf file. We use a template to compensate for that.
if cmp_os_release < 'victoria':
resource_map[NOVA_COMPUTE_CONF] = {
"services": ["nova-compute"],
"contexts": [],
}
cmp_distro_codename = CompareHostReleases(
lsb_release()['DISTRIB_CODENAME'].lower())
if (cmp_distro_codename >= 'yakkety' or cmp_os_release >= 'ocata'):
@ -391,7 +406,8 @@ def resource_map():
# NOTE(james-page): If not on an upstart based system, don't write
# and override file for libvirt-bin.
if not os.path.exists('/etc/init'):
del resource_map[LIBVIRT_BIN_OVERRIDES]
if LIBVIRT_BIN_OVERRIDES in resource_map:
del resource_map[LIBVIRT_BIN_OVERRIDES]
return resource_map
@ -463,6 +479,18 @@ def determine_packages():
packages.append('ceph-common')
virt_type = config('virt-type')
if virt_type == 'ironic' and release < 'victoria':
# ironic compute driver is part of nova and
# gets installed allong with python3-nova
# The nova-compute-ironic metapackage that satisfies
# nova-compute-hypervisor does not exist for versions of
# OpenStack prior to Victoria. Use nova-compute-vmware,
# as that package has the least amount of dependencies.
# We also add python3-ironicclient here. This is a dependency
# which gets installed by nova-compute-ironic in Victoria and later.
VIRT_TYPES[virt_type] = [
'nova-compute-vmware',
'python3-ironicclient']
try:
packages.extend(VIRT_TYPES[virt_type])
except KeyError:
@ -876,6 +904,8 @@ def get_optional_relations():
optional_interfaces['neutron-plugin'] = ['neutron-plugin']
if config('encrypt'):
optional_interfaces['vault'] = ['secrets-storage']
if config('virt-type').lower() == 'ironic':
optional_interfaces['baremetal'] = ['ironic-api']
return optional_interfaces

View File

@ -53,6 +53,8 @@ requires:
interface: keystone-credentials
secrets-storage:
interface: vault-kv
ironic-api:
interface: baremetal
peers:
compute-peer:
interface: nova

View File

@ -0,0 +1,15 @@
{% if virt_type == 'ironic' and auth_host and ironic_api_ready -%}
{% if api_version and api_version == "3" -%}
{% set auth_ver = "v3" -%}
{% else -%}
{% set auth_ver = "v2.0" -%}
{% endif -%}
[ironic]
auth_type = password
auth_url = {{auth_protocol}}://{{auth_host}}:{{auth_port}}/{{auth_ver}}
project_name = {{ admin_tenant_name }}
username = {{ admin_user }}
password = {{ admin_password }}
project_domain_name = {{ admin_domain_name }}
user_domain_name = {{ admin_domain_name }}
{% endif -%}

View File

@ -0,0 +1,2 @@
[DEFAULT]
compute_driver=ironic.IronicDriver

View File

@ -316,6 +316,8 @@ disable_libvirt_livesnapshot = False
{% include "parts/section-placement" %}
{% include "parts/section-ironic" %}
[compute]
{% if cpu_shared_set -%}
cpu_shared_set = {{ cpu_shared_set }}

View File

@ -767,6 +767,37 @@ class NovaComputeContextTests(CharmTestCase):
'pcid, vmx, pdpe1gb')
class IronicAPIContextTests(CharmTestCase):
def setUp(self):
super(IronicAPIContextTests, self).setUp(context, TO_PATCH)
self.relation_get.side_effect = self.test_relation.get
def test_ironic_api_ready_no_relation(self):
self.relation_ids.return_value = []
self.assertEqual(
context.IronicAPIContext()(), {})
def test_ironic_api_ready(self):
self.relation_ids.return_value = ['ironic-api:0']
self.related_units.return_value = 'ironic-api/0'
self.test_relation.set({
'ironic-api-ready': True,
})
self.assertEqual(
context.IronicAPIContext()(),
{'ironic_api_ready': True})
def test_ironic_api_not_ready(self):
self.relation_ids.return_value = ['ironic-api:0']
self.related_units.return_value = 'ironic-api/0'
self.test_relation.set({
'ready': False,
})
self.assertEqual(
context.IronicAPIContext()(), {})
class SerialConsoleContextTests(CharmTestCase):
def setUp(self):

View File

@ -101,6 +101,31 @@ class NovaComputeUtilsTests(CharmTestCase):
]
self.assertTrue(ex == result)
@patch.object(utils, 'nova_metadata_requirement')
def test_determine_packages_ironic(self, en_meta):
self.os_release.return_value = 'victoria'
self.test_config.set('virt-type', 'ironic')
en_meta.return_value = (False, None)
self.relation_ids.return_value = []
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-compute-ironic'
]
self.assertTrue(ex.sort() == result.sort())
@patch.object(utils, 'nova_metadata_requirement')
def test_determine_packages_ironic_pre_victoria(self, en_meta):
self.os_release.return_value = 'train'
self.test_config.set('virt-type', 'ironic')
en_meta.return_value = (False, None)
self.relation_ids.return_value = []
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-compute-vmware',
'python3-ironicclient'
]
self.assertTrue(ex.sort() == result.sort())
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
@ -526,6 +551,28 @@ class NovaComputeUtilsTests(CharmTestCase):
result = utils.resource_map()['/etc/nova/nova.conf']['services']
self.assertTrue('nova-api-metadata' in result)
@patch.object(utils, 'nova_metadata_requirement')
def test_resource_map_ironic_pre_victoria(self, _metadata):
_metadata.return_value = (True, None)
self.relation_ids.return_value = []
self.os_release.return_value = 'train'
self.test_config.set('virt-type', 'ironic')
result = utils.resource_map()
self.assertTrue(utils.NOVA_COMPUTE_CONF in result)
self.assertEqual(
result[utils.NOVA_COMPUTE_CONF]["services"], ["nova-compute"])
self.assertEqual(
result[utils.NOVA_COMPUTE_CONF]["contexts"], [])
@patch.object(utils, 'nova_metadata_requirement')
def test_resource_map_ironic(self, _metadata):
_metadata.return_value = (True, None)
self.relation_ids.return_value = []
self.os_release.return_value = 'victoria'
self.test_config.set('virt-type', 'ironic')
result = utils.resource_map()
self.assertTrue(utils.NOVA_COMPUTE_CONF not in result)
def fake_user(self, username='foo'):
user = MagicMock()
user.pw_dir = '/home/' + username