Merge "XenAPI: Add the support for updating the status of the host."
This commit is contained in:
@@ -33,7 +33,8 @@ class XenAPIDriverTestCase(stubs.XenAPITestBaseNoDB):
|
||||
'host_hostname': 'somename',
|
||||
'supported_instances': 'x86_64',
|
||||
'host_cpu_info': {'cpu_count': 50},
|
||||
'vcpus_used': 10}
|
||||
'vcpus_used': 10,
|
||||
'pci_passthrough_devices': ''}
|
||||
|
||||
def test_available_resource(self):
|
||||
self.flags(connection_url='test_url',
|
||||
|
||||
@@ -2034,6 +2034,18 @@ class XenAPIHostTestCase(stubs.XenAPITestBase):
|
||||
stats = self.conn.get_host_stats(True)
|
||||
self.assertEqual(stats['vcpus_used'], 4)
|
||||
|
||||
def test_pci_passthrough_devices_whitelist(self):
|
||||
# NOTE(guillaume-thouvenin): This pci whitelist will be used to
|
||||
# match with _plugin_xenhost_get_pci_device_details method in fake.py.
|
||||
self.flags(pci_passthrough_whitelist=
|
||||
['[{"vendor_id":"10de", "product_id":"11bf"}]'])
|
||||
stats = self.conn.get_host_stats()
|
||||
self.assertEqual(len(stats['pci_passthrough_devices']), 1)
|
||||
|
||||
def test_pci_passthrough_devices_no_whitelist(self):
|
||||
stats = self.conn.get_host_stats()
|
||||
self.assertEqual(len(stats['pci_passthrough_devices']), 0)
|
||||
|
||||
def test_host_state_missing_sr(self):
|
||||
def fake_safe_find_sr(session):
|
||||
raise exception.StorageRepositoryNotFound('not there')
|
||||
|
||||
@@ -59,7 +59,7 @@ class XenAPISession(object):
|
||||
# changed in development environments.
|
||||
# MAJOR VERSION: Incompatible changes with the plugins
|
||||
# MINOR VERSION: Compatible changes, new plguins, etc
|
||||
PLUGIN_REQUIRED_VERSION = '1.1'
|
||||
PLUGIN_REQUIRED_VERSION = '1.2'
|
||||
|
||||
def __init__(self, url, user, pw):
|
||||
import XenAPI
|
||||
|
||||
@@ -482,7 +482,9 @@ class XenAPIDriver(driver.ComputeDriver):
|
||||
# arch_filter.py - see libvirt/driver.py get_cpu_info
|
||||
'cpu_info': jsonutils.dumps(host_stats['host_cpu_info']),
|
||||
'supported_instances': jsonutils.dumps(
|
||||
host_stats['supported_instances'])}
|
||||
host_stats['supported_instances']),
|
||||
'pci_passthrough_devices': jsonutils.dumps(
|
||||
host_stats['pci_passthrough_devices'])}
|
||||
|
||||
return dic
|
||||
|
||||
|
||||
@@ -689,6 +689,33 @@ class SessionBase(object):
|
||||
def _plugin_xenhost_host_uptime(self, method, args):
|
||||
return jsonutils.dumps({"uptime": "fake uptime"})
|
||||
|
||||
def _plugin_xenhost_get_pci_device_details(self, method, args):
|
||||
"""Simulate the ouput of three pci devices.
|
||||
|
||||
Both of those devices are available for pci passtrough but
|
||||
only one will match with the pci whitelist used in the
|
||||
method test_pci_passthrough_devices_*().
|
||||
Return a single list.
|
||||
|
||||
"""
|
||||
# Driver is not pciback
|
||||
dev_bad1 = ["Slot:\t86:10.0", "Class:\t0604", "Vendor:\t10b5",
|
||||
"Device:\t8747", "Rev:\tba", "Driver:\tpcieport", "\n"]
|
||||
# Driver is pciback but vendor and device are bad
|
||||
dev_bad2 = ["Slot:\t88:00.0", "Class:\t0300", "Vendor:\t0bad",
|
||||
"Device:\tcafe", "SVendor:\t10de", "SDevice:\t100d",
|
||||
"Rev:\ta1", "Driver:\tpciback", "\n"]
|
||||
# Driver is pciback and vendor, device are used for matching
|
||||
dev_good = ["Slot:\t87:00.0", "Class:\t0300", "Vendor:\t10de",
|
||||
"Device:\t11bf", "SVendor:\t10de", "SDevice:\t100d",
|
||||
"Rev:\ta1", "Driver:\tpciback", "\n"]
|
||||
|
||||
lspci_output = "\n".join(dev_bad1 + dev_bad2 + dev_good)
|
||||
return pickle.dumps(lspci_output)
|
||||
|
||||
def _plugin_xenhost_get_pci_type(self, method, args):
|
||||
return pickle.dumps("type-PCI")
|
||||
|
||||
def _plugin_console_get_console_log(self, method, args):
|
||||
dom_id = args["dom_id"]
|
||||
if dom_id == 0:
|
||||
@@ -696,7 +723,7 @@ class SessionBase(object):
|
||||
return base64.b64encode(zlib.compress("dom_id: %s" % dom_id))
|
||||
|
||||
def _plugin_nova_plugin_version_get_version(self, method, args):
|
||||
return pickle.dumps("1.1")
|
||||
return pickle.dumps("1.2")
|
||||
|
||||
def _plugin_xenhost_query_gc(self, method, args):
|
||||
return pickle.dumps("False")
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
Management class for host-related functions (start, reboot, etc).
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from nova.compute import task_states
|
||||
from nova.compute import vm_states
|
||||
from nova import conductor
|
||||
@@ -27,6 +29,7 @@ from nova.objects import instance as instance_obj
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.pci import pci_whitelist
|
||||
from nova.virt.xenapi import pool_states
|
||||
from nova.virt.xenapi import vm_utils
|
||||
|
||||
@@ -149,8 +152,84 @@ class HostState(object):
|
||||
super(HostState, self).__init__()
|
||||
self._session = session
|
||||
self._stats = {}
|
||||
self._pci_device_filter = pci_whitelist.get_pci_devices_filter()
|
||||
self.update_status()
|
||||
|
||||
def _get_passthrough_devices(self):
|
||||
"""Get a list pci devices that are available for pci passthtough.
|
||||
|
||||
We use a plugin to get the output of the lspci command runs on dom0.
|
||||
From this list we will extract pci devices that are using the pciback
|
||||
kernel driver. Then we compare this list to the pci whitelist to get
|
||||
a new list of pci devices that can be used for pci passthrough.
|
||||
|
||||
:returns: a list of pci devices available for pci passthrough.
|
||||
"""
|
||||
def _compile_hex(pattern):
|
||||
"""
|
||||
Return a compiled regular expression pattern into which we have
|
||||
replaced occurences of hex by [\da-fA-F].
|
||||
"""
|
||||
return re.compile(pattern.replace("hex", r"[\da-fA-F]"))
|
||||
|
||||
def _parse_pci_device_string(dev_string):
|
||||
"""
|
||||
Exctract information from the device string about the slot, the
|
||||
vendor and the product ID. The string is as follow:
|
||||
"Slot:\tBDF\nClass:\txxxx\nVendor:\txxxx\nDevice:\txxxx\n..."
|
||||
Return a dictionary with informations about the device.
|
||||
"""
|
||||
slot_regex = _compile_hex(r"Slot:\t"
|
||||
r"((?:hex{4}:)?" # Domain: (optional)
|
||||
r"hex{2}:" # Bus:
|
||||
r"hex{2}\." # Device.
|
||||
r"hex{1})") # Function
|
||||
vendor_regex = _compile_hex(r"\nVendor:\t(hex+)")
|
||||
product_regex = _compile_hex(r"\nDevice:\t(hex+)")
|
||||
|
||||
slot_id = slot_regex.findall(dev_string)
|
||||
vendor_id = vendor_regex.findall(dev_string)
|
||||
product_id = product_regex.findall(dev_string)
|
||||
|
||||
if not slot_id or not vendor_id or not product_id:
|
||||
raise exception.NovaException(
|
||||
_("Failed to parse information about"
|
||||
" a pci device for passthrough"))
|
||||
|
||||
type_pci = self._session.call_plugin_serialized(
|
||||
'xenhost', 'get_pci_type', slot_id[0])
|
||||
|
||||
return {'label': '_'.join(['label',
|
||||
vendor_id[0],
|
||||
product_id[0]]),
|
||||
'vendor_id': vendor_id[0],
|
||||
'product_id': product_id[0],
|
||||
'address': slot_id[0],
|
||||
'dev_id': '_'.join(['pci', slot_id[0]]),
|
||||
'dev_type': type_pci,
|
||||
'status': 'available'}
|
||||
|
||||
# Devices are separated by a blank line. That is why we
|
||||
# use "\n\n" as separator.
|
||||
lspci_out = self._session.call_plugin_serialized(
|
||||
'xenhost', 'get_pci_device_details')
|
||||
pci_list = lspci_out.split("\n\n")
|
||||
|
||||
# For each device of the list, check if it uses the pciback
|
||||
# kernel driver and if it does, get informations and add it
|
||||
# to the list of passthrough_devices. Ignore it if the driver
|
||||
# is not pciback.
|
||||
passthrough_devices = []
|
||||
|
||||
for dev_string_info in pci_list:
|
||||
if "Driver:\tpciback" in dev_string_info:
|
||||
new_dev = _parse_pci_device_string(dev_string_info)
|
||||
|
||||
if self._pci_device_filter.device_assignable(new_dev):
|
||||
passthrough_devices.append(new_dev)
|
||||
|
||||
return passthrough_devices
|
||||
|
||||
def get_host_stats(self, refresh=False):
|
||||
"""Return the current state of the host. If 'refresh' is
|
||||
True, run the update first.
|
||||
@@ -196,6 +275,7 @@ class HostState(object):
|
||||
for vm_ref, vm_rec in vm_utils.list_vms(self._session):
|
||||
vcpus_used = vcpus_used + int(vm_rec['VCPUs_max'])
|
||||
data['vcpus_used'] = vcpus_used
|
||||
data['pci_passthrough_devices'] = self._get_passthrough_devices()
|
||||
self._stats = data
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user