Merge "XenAPI: Add the support for updating the status of the host."

This commit is contained in:
Jenkins
2014-02-19 23:56:38 +00:00
committed by Gerrit Code Review
8 changed files with 162 additions and 8 deletions

View File

@@ -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',

View File

@@ -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')

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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