Merge "Add configurable libvirt firmware"

This commit is contained in:
Zuul 2019-02-13 15:12:55 +00:00 committed by Gerrit Code Review
commit 4b707299e0
7 changed files with 99 additions and 39 deletions

View File

@ -18,3 +18,16 @@ SUSHY_EMULATOR_OS_CLOUD = None
# The libvirt URI to use. This option enables libvirt driver.
SUSHY_EMULATOR_LIBVIRT_URI = 'qemu:///system'
# The map of firmware loaders dependant on the boot mode and
# system architecture
SUSHY_EMULATOR_BOOT_LOADER_MAP = {
'Uefi': {
'x86_64': '/usr/share/OVMF/OVMF_CODE.fd',
'aarch64': '/usr/share/AAVMF/AAVMF_CODE.fd'
},
'Legacy': {
'x86_64': None,
'aarch64': None
}
}

View File

@ -85,16 +85,16 @@ class LibvirtDriver(AbstractDriver):
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
# NOTE(etingof): we have these firmware blobs hardcoded here
# which seems to work for CentOS, for instance.
# What's not clear is how to adapt to possible different
# boot loaders paths dependent on libvirt packaging and custom
# domain configuration...
BOOT_LOADER_MAP = {
'Uefi': {
'x86_64': '/usr/share/OVMF/OVMF_CODE.fd',
'aarch64': '/usr/share/AAVMF/AAVMF_CODE.fd'
},
'Legacy': {
'x86_64': None,
'aarch64': None
}
}
DEFAULT_BIOS_ATTRIBUTES = {"BootMode": "Uefi",
@ -102,8 +102,14 @@ class LibvirtDriver(AbstractDriver):
"NicBoot1": "NetworkBoot",
"ProcTurboMode": "Enabled"}
def __init__(self, uri=None):
def __init__(self, config, uri=None):
self._config = config
self._uri = uri or self.LIBVIRT_URI
self.BOOT_LOADER_MAP = self._config.get(
'SUSHY_EMULATOR_BOOT_LOADER_MAP', self.BOOT_LOADER_MAP)
self.KNOWN_BOOT_LOADERS = set(y
for x in self.BOOT_LOADER_MAP.values()
for y in x.values())
def _get_domain(self, identity, readonly=False):
with libvirt_open(self._uri, readonly=readonly) as conn:
@ -325,6 +331,8 @@ class LibvirtDriver(AbstractDriver):
raise error.FishyError(msg)
loaders = []
for os_element in tree.findall('os'):
type_element = os_element.find('type')
if type_element is None:
@ -333,34 +341,57 @@ class LibvirtDriver(AbstractDriver):
os_arch = type_element.get('arch')
try:
loader_path = self.BOOT_LOADER_MAP[boot_mode][os_arch]
loader_path = self.BOOT_DEVICE_MAP[boot_mode][os_arch]
except KeyError:
# NOTE(etingof): assume no specific boot loader
logger.warning('Boot loader is not configured for '
'boot mode %s and OS architecture %s. '
'Assuming no specific boot loader.',
boot_mode, os_arch)
loader_path = ''
# Update all "loader" elements
# NOTE(etingof): here we collect all tree nodes to be
# updated, but do not update them yet. The reason is to
# make sure that previously configured boot loaders are
# all present in our configuration, so we won't lose it
# when setting ours.
for loader_element in os_element.findall('loader'):
loader_element.set('type', loader_type)
# NOTE(etingof): here we override previous boot loader for
# for the domain. If it's different than what we have
# hardcoded in the BOOT_LOADER_MAP, we won't be able to
# revert back to the original boor loader should we change
# domain boot mode.
loader_element.text = loader_path
with libvirt_open(self._uri) as conn:
try:
conn.defineXML(ET.tostring(tree).decode('utf-8'))
except libvirt.libvirtError as e:
msg = ('Error changing boot mode at libvirt URI '
'"%(uri)s": %(error)s' % {'uri': self._uri,
'error': e})
if loader_element.text not in self.KNOWN_BOOT_LOADERS:
msg = ('Unknown boot loader path "%(path)s" in domain '
'"%(identity)s" configuration encountered while '
'setting boot mode "%(mode)s", system architecture '
'"%(arch)s". Consider adding this loader path to '
'emulator config.' % {'identity': identity,
'mode': boot_mode,
'arch': os_arch,
'path': loader_element.text})
raise error.FishyError(msg)
loaders.append((loader_element, loader_path))
if not loaders:
msg = ('Can\'t set boot mode because no "os" elements are present '
'in domain "%(identity)s" or not a single "os" element has '
'"loader" configured.' % {'identity': identity})
raise error.FishyError(msg)
for loader_element, loader_path in loaders:
loader_element.set('type', loader_type)
loader_element.text = loader_path
with libvirt_open(self._uri) as conn:
try:
conn.defineXML(ET.tostring(tree).decode('utf-8'))
except libvirt.libvirtError as e:
msg = ('Error changing boot mode at libvirt URI '
'"%(uri)s": %(error)s' % {'uri': self._uri,
'error': e})
raise error.FishyError(msg)
def get_total_memory(self, identity):
"""Get computer system total memory

View File

@ -51,9 +51,10 @@ class OpenStackDriver(AbstractDriver):
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
def __init__(self, os_cloud, readonly=False):
def __init__(self, config, os_cloud, readonly=False):
self._cc = openstack.connect(cloud=os_cloud)
self._os_cloud = os_cloud
self._config = config
def _get_instance(self, identity):
instance = self._cc.get_server(identity)

View File

@ -46,7 +46,8 @@ def init_virt_driver(decorated_func):
app.logger.error('Nova driver not loaded')
sys.exit(1)
driver = novadriver.OpenStackDriver(os.environ['OS_CLOUD'])
driver = novadriver.OpenStackDriver(
app.config, os.environ['OS_CLOUD'])
else:
if not libvirtdriver.is_loaded:
@ -54,6 +55,7 @@ def init_virt_driver(decorated_func):
sys.exit(1)
driver = libvirtdriver.LibvirtDriver(
app.config,
os.environ.get(
'SUSHY_EMULATOR_LIBVIRT_URI',
# NOTE(etingof): left for backward compatibility
@ -310,7 +312,7 @@ def main():
app.logger.error('Nova driver not loaded')
return 1
driver = novadriver.OpenStackDriver(os_cloud)
driver = novadriver.OpenStackDriver(app.config, os_cloud)
else:
if not libvirtdriver.is_loaded:
@ -318,6 +320,7 @@ def main():
return 1
driver = libvirtdriver.LibvirtDriver(
app.config,
args.libvirt_uri or
app.config.get('SUSHY_EMULATOR_LIBVIRT_URI', '')
)

View File

@ -5,7 +5,7 @@
<currentMemory>219200</currentMemory>
<vcpu>2</vcpu>
<os>
<type arch='i686' machine='pc'>hvm</type>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='cdrom'/>
<loader type='rom'/>
</os>

View File

@ -25,7 +25,7 @@ class LibvirtDriverTestCase(base.BaseTestCase):
uuid = 'c7a5fdbd-cdaf-9455-926a-d65c16db1809'
def setUp(self):
self.test_driver = LibvirtDriver()
self.test_driver = LibvirtDriver({})
super(LibvirtDriverTestCase, self).setUp()
@mock.patch('libvirt.open', autospec=True)
@ -211,6 +211,22 @@ class LibvirtDriverTestCase(base.BaseTestCase):
conn_mock = libvirt_rw_mock.return_value
conn_mock.defineXML.assert_called_once_with(mock.ANY)
@mock.patch('libvirt.open', autospec=True)
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_set_boot_mode_unknown_path(self, libvirt_mock, libvirt_rw_mock):
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
data = f.read()
conn_mock = libvirt_mock.return_value
domain_mock = conn_mock.lookupByUUID.return_value
domain_mock.XMLDesc.return_value = data
with mock.patch.dict(
self.test_driver.KNOWN_BOOT_LOADERS, {}, clear=True):
self.assertRaises(
error.FishyError, self.test_driver.set_boot_mode,
self.uuid, 'Uefi')
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_get_total_memory(self, libvirt_mock):
conn_mock = libvirt_mock.return_value

View File

@ -30,7 +30,7 @@ class NovaDriverTestCase(base.BaseTestCase):
self.nova_patcher = mock.patch('openstack.connect', autospec=True)
self.nova_mock = self.nova_patcher.start()
self.test_driver = OpenStackDriver('fake-cloud')
self.test_driver = OpenStackDriver({}, 'fake-cloud')
super(NovaDriverTestCase, self).setUp()
@ -216,10 +216,7 @@ class NovaDriverTestCase(base.BaseTestCase):
server = mock.Mock(id=self.uuid, addresses=addresses)
self.nova_mock.return_value.get_server.return_value = server
test_driver = OpenStackDriver('fake-cloud')
nics = test_driver.get_nics(self.uuid)
nics = self.test_driver.get_nics(self.uuid)
self.assertEqual([{'id': 'fa:16:3e:22:18:31',
'mac': 'fa:16:3e:22:18:31'},
{'id': 'fa:16:3e:46:e3:ac',
@ -229,8 +226,7 @@ class NovaDriverTestCase(base.BaseTestCase):
def test_get_nics_empty(self):
server = mock.Mock(id=self.uuid, addresses=None)
self.nova_mock.return_value.get_server.return_value = server
test_driver = OpenStackDriver('fake-cloud')
nics = test_driver.get_nics(self.uuid)
nics = self.test_driver.get_nics(self.uuid)
self.assertEqual(set(), nics)
def test_get_nics_error(self):