Merge "Add scheduler support for PCI passthrough"
This commit is contained in:
@@ -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
|
||||
|
42
nova/scheduler/filters/pci_passthrough_filter.py
Normal file
42
nova/scheduler/filters/pci_passthrough_filter.py
Normal 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'))
|
@@ -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
|
||||
@@ -1537,3 +1538,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")
|
||||
|
||||
|
Reference in New Issue
Block a user