Merge "Add scheduler support for PCI passthrough"

This commit is contained in:
Jenkins
2013-09-03 18:28:29 +00:00
committed by Gerrit Code Review
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
@@ -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))

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