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:
parent
7c9e6a8a39
commit
ff69372263
|
@ -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
|
||||
|
|
|
@ -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'))
|
|
@ -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 [
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
Loading…
Reference in New Issue