Add scheduler support for PCI passthrough

PCI tracker updates the PCI stats information which provide an abstract
resource view to the scheduler

Openstack clients request PCI devices using PCI aliases, which are
converted to PCI requests. The PCI passthrough filter checks the PCI stats
for nodes that can satisfy the request of the instance, and gives the
schedule result.

bp:pci-passthrough-base

Change-Id: I0ede4caefab8b22126d0c8b9293f023aa4780e36
Signed-off-by: Yongli He <yongli.he@intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
This commit is contained in:
He Yongli 2013-08-01 17:25:45 +08:00
parent 7c9e6a8a39
commit ff69372263
6 changed files with 144 additions and 6 deletions

View File

@ -30,6 +30,7 @@ from nova import exception
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier
from nova.pci import pci_request
from nova.scheduler import driver
from nova.scheduler import scheduler_options
from nova.scheduler import utils as scheduler_utils
@ -212,6 +213,10 @@ class FilterScheduler(driver.Scheduler):
os_type = request_spec['instance_properties']['os_type']
filter_properties['project_id'] = project_id
filter_properties['os_type'] = os_type
pci_requests = pci_request.get_pci_requests_from_flavor(
request_spec.get('instance_type') or {})
if pci_requests:
filter_properties['pci_requests'] = pci_requests
def _max_attempts(self):
max_attempts = CONF.scheduler_max_attempts

View File

@ -0,0 +1,42 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2013 ISP RAS.
# 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 nova.scheduler import filters
class PciPassthroughFilter(filters.BaseHostFilter):
"""Pci Passthrough Filter based on PCI request
Filter that schedules tasks on a host if the host has devices
to meet the device requests in the 'extra_specs' for the flavor.
PCI resource tracker provides updated summary information about the
PCI devices for each host, like:
[{"count": 5, "vendor_id": "8086", "product_id": "1520",
"extra_info":'{}'}],
and VM requests PCI devices via PCI requests, like:
[{"count": 1, "vendor_id": "8086", "product_id": "1520",}].
The filter checkes if the host passes or not based on these information.
"""
def host_passes(self, host_state, filter_properties):
"""Return true if the host has the required PCI devices."""
if not filter_properties.get('pci_requests'):
return True
return host_state.pci_stats.support_requests(
filter_properties.get('pci_requests'))

View File

@ -29,6 +29,8 @@ from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova.pci import pci_request
from nova.pci import pci_stats
from nova.scheduler import filters
from nova.scheduler import weights
@ -166,6 +168,10 @@ class HostState(object):
self.vcpus_total = compute['vcpus']
self.vcpus_used = compute['vcpus_used']
self.updated = compute['updated_at']
if hasattr(compute, 'pci_stats'):
self.pci_stats = pci_stats.PciDeviceStats(compute['pci_stats'])
else:
self.pci_stats = None
# All virt drivers report host_ip
self.host_ip = compute['host_ip']
@ -252,6 +258,10 @@ class HostState(object):
self.num_instances_by_os_type[os_type] = 0
self.num_instances_by_os_type[os_type] += 1
pci_requests = pci_request.get_instance_pci_requests(instance)
if pci_requests and self.pci_stats:
self.pci_stats.apply_requests(pci_requests)
vm_state = instance.get('vm_state', vm_states.BUILDING)
task_state = instance.get('task_state')
if vm_state == vm_states.BUILDING or task_state in [

View File

@ -25,6 +25,7 @@ from nova.conductor import api as conductor_api
from nova import context
from nova import db
from nova import exception
from nova.pci import pci_request
from nova.scheduler import driver
from nova.scheduler import filter_scheduler
from nova.scheduler import host_manager
@ -216,7 +217,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
sched = fakes.FakeFilterScheduler()
instance_properties = {'project_id': '12345', 'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
filter_properties = {}
self.mox.StubOutWithMock(db, 'compute_node_get_all')
@ -273,7 +275,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
sched = fakes.FakeFilterScheduler()
instance_properties = {'project_id': '12345', 'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
filter_properties = {}
self.mox.StubOutWithMock(db, 'compute_node_get_all')
@ -292,7 +295,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
sched = fakes.FakeFilterScheduler()
instance_properties = {'project_id': '12345', 'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
retry = dict(num_attempts=1)
filter_properties = dict(retry=retry)
@ -443,7 +447,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
'vcpus': 1,
'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
filter_properties = {}
self.mox.ReplayAll()
hosts = sched._schedule(self.context, request_spec,
@ -472,7 +477,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
'ephemeral_gb': 0,
'vcpus': 1,
'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
filter_properties = {}
self.mox.ReplayAll()
hosts = sched._schedule(self.context, request_spec,
@ -511,7 +517,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
'vcpus': 1,
'os_type': 'Linux'}
request_spec = dict(instance_properties=instance_properties)
request_spec = dict(instance_properties=instance_properties,
instance_type={})
self.stubs.Set(weights.HostWeightHandler,
'get_weighed_objects', _fake_weigh_objects)
@ -656,3 +663,19 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
sched._provision_resource(fake_context, weighted_host,
request_spec, filter_properties,
None, None, None, None)
def test_pci_request_in_filter_properties(self):
instance_type = {}
request_spec = {'instance_type': instance_type,
'instance_properties': {'project_id': 1,
'os_type': 'Linux'}}
filter_properties = {}
requests = [{'count': 1, 'spec': [{'vendor_id': '8086'}]}]
self.mox.StubOutWithMock(pci_request, 'get_pci_requests_from_flavor')
pci_request.get_pci_requests_from_flavor(
instance_type).AndReturn(requests)
self.mox.ReplayAll()
self.driver.populate_filter_properties(
request_spec, filter_properties)
self.assertEqual(filter_properties.get('pci_requests'),
requests)

View File

@ -24,6 +24,7 @@ from nova import context
from nova import db
from nova.openstack.common import jsonutils
from nova.openstack.common import timeutils
from nova.pci import pci_stats
from nova.scheduler import filters
from nova.scheduler.filters import extra_specs_ops
from nova.scheduler.filters import trusted_filter
@ -1522,3 +1523,39 @@ class HostFiltersTestCase(test.NoDBTestCase):
'project_id': 'my_tenantid'}}}
host = fakes.FakeHostState('host1', 'compute', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def _fake_pci_support_requests(self, pci_requests):
self.pci_requests = pci_requests
return self.pci_request_result
def test_pci_passthrough_pass(self):
filt_cls = self.class_map['PciPassthroughFilter']()
requests = [{'count': 1, 'spec': [{'vendor_id': '8086'}]}]
filter_properties = {'pci_requests': requests}
self.stubs.Set(pci_stats.PciDeviceStats, 'support_requests',
self._fake_pci_support_requests)
host = fakes.FakeHostState(
'host1', 'node1',
attribute_dict={'pci_stats': pci_stats.PciDeviceStats()})
self.pci_request_result = True
self.assertTrue(filt_cls.host_passes(host, filter_properties))
self.assertEqual(self.pci_requests, requests)
def test_pci_passthrough_fail(self):
filt_cls = self.class_map['PciPassthroughFilter']()
requests = [{'count': 1, 'spec': [{'vendor_id': '8086'}]}]
filter_properties = {'pci_requests': requests}
self.stubs.Set(pci_stats.PciDeviceStats, 'support_requests',
self._fake_pci_support_requests)
host = fakes.FakeHostState(
'host1', 'node1',
attribute_dict={'pci_stats': pci_stats.PciDeviceStats()})
self.pci_request_result = False
self.assertFalse(filt_cls.host_passes(host, filter_properties))
self.assertEqual(self.pci_requests, requests)
def test_pci_passthrough_no_pci_request(self):
filt_cls = self.class_map['PciPassthroughFilter']()
filter_properties = {}
host = fakes.FakeHostState('h1', 'n1', {})
self.assertTrue(filt_cls.host_passes(host, filter_properties))

View File

@ -464,6 +464,27 @@ class HostStateTestCase(test.NoDBTestCase):
self.assertEqual('cpu_info', host.cpu_info)
self.assertEqual({}, host.supported_instances)
def test_stat_consumption_from_compute_node_non_pci(self):
stats = [
dict(key='num_instances', value='5'),
dict(key='num_proj_12345', value='3'),
dict(key='num_proj_23456', value='1'),
dict(key='num_vm_%s' % vm_states.BUILDING, value='2'),
dict(key='num_vm_%s' % vm_states.SUSPENDED, value='1'),
dict(key='num_task_%s' % task_states.RESIZE_MIGRATING, value='1'),
dict(key='num_task_%s' % task_states.MIGRATING, value='2'),
dict(key='num_os_type_linux', value='4'),
dict(key='num_os_type_windoze', value='1'),
dict(key='io_workload', value='42'),
]
compute = dict(stats=stats, memory_mb=0, free_disk_gb=0, local_gb=0,
local_gb_used=0, free_ram_mb=0, vcpus=0, vcpus_used=0,
updated_at=None, host_ip='127.0.0.1')
host = host_manager.HostState("fakehost", "fakenode")
host.update_from_compute_node(compute)
self.assertEqual(None, host.pci_stats)
def test_stat_consumption_from_instance(self):
host = host_manager.HostState("fakehost", "fakenode")