Merge "Adding support for multiple hypervisor versions"

This commit is contained in:
Jenkins
2013-10-30 09:21:45 +00:00
committed by Gerrit Code Review
8 changed files with 159 additions and 48 deletions

View File

@@ -14,10 +14,12 @@
# 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 distutils import versionpredicate
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova.scheduler import filters
from nova import utils
LOG = logging.getLogger(__name__)
@@ -36,7 +38,8 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
# a request
run_filter_once_per_request = True
def _instance_supported(self, host_state, image_props):
def _instance_supported(self, host_state, image_props,
hypervisor_version):
img_arch = image_props.get('architecture', None)
img_h_type = image_props.get('hypervisor_type', None)
img_vm_mode = image_props.get('vm_mode', None)
@@ -53,7 +56,7 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
LOG.debug(_("Instance contains properties %(image_props)s, "
"but no corresponding supported_instances are "
"advertised by the compute node"),
{'image_props': image_props})
{'image_props': image_props})
return False
def _compare_props(props, other_props):
@@ -62,20 +65,34 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
return False
return True
def _compare_product_version(hyper_version, image_props):
version_required = image_props.get('hypervisor_version_requires')
if not(hypervisor_version and version_required):
return True
img_prop_predicate = versionpredicate.VersionPredicate(
'image_prop (%s)' % version_required)
hyper_ver_str = utils.convert_version_to_str(hyper_version)
return img_prop_predicate.satisfied_by(hyper_ver_str)
for supp_inst in supp_instances:
if _compare_props(checked_img_props, supp_inst):
LOG.debug(_("Instance properties %(image_props)s "
"are satisfied by compute host supported_instances"
"%(supp_instances)s"),
{'image_props': image_props,
'supp_instances': supp_instances})
return True
if _compare_product_version(hypervisor_version, image_props):
LOG.debug(_("Instance properties %(image_props)s "
"are satisfied by compute host hypervisor "
"version %(hypervisor_version) and "
"supported instances %(supp_instances)s"),
{'image_props': image_props,
'supp_instances': supp_instances,
'hypervisor_version': hypervisor_version})
return True
LOG.debug(_("Instance contains properties %(image_props)s "
"that are not provided by the compute node "
"supported_instances %(supp_instances)s"),
"supported_instances %(supp_instances)s or "
"hypervisor version %(hypervisor_version)s do not match"),
{'image_props': image_props,
'supp_instances': supp_instances})
'supp_instances': supp_instances,
'hypervisor_version': hypervisor_version})
return False
def host_passes(self, host_state, filter_properties):
@@ -87,7 +104,8 @@ class ImagePropertiesFilter(filters.BaseHostFilter):
spec = filter_properties.get('request_spec', {})
image_props = spec.get('image', {}).get('properties', {})
if not self._instance_supported(host_state, image_props):
if not self._instance_supported(host_state, image_props,
host_state.hypervisor_version):
LOG.debug(_("%(host_state)s does not support requested "
"instance_properties"), {'host_state': host_state})
return False

View File

@@ -187,6 +187,7 @@ class HostState(object):
# overwrite any values, or get overwritten themselves. Store in self so
# filters can schedule with them.
self.stats = self._statmap(compute.get('stats', []))
self.hypervisor_version = compute['hypervisor_version']
# Track number of instances on host
self.num_instances = int(self.stats.get('num_instances', 0))

View File

@@ -29,22 +29,26 @@ COMPUTE_NODES = [
disk_available_least=512, free_ram_mb=512, vcpus_used=1,
free_disk_mb=512, local_gb_used=0, updated_at=None,
service=dict(host='host1', disabled=False),
hypervisor_hostname='node1', host_ip='127.0.0.1'),
hypervisor_hostname='node1', host_ip='127.0.0.1',
hypervisor_version=0),
dict(id=2, local_gb=2048, memory_mb=2048, vcpus=2,
disk_available_least=1024, free_ram_mb=1024, vcpus_used=2,
free_disk_mb=1024, local_gb_used=0, updated_at=None,
service=dict(host='host2', disabled=True),
hypervisor_hostname='node2', host_ip='127.0.0.1'),
hypervisor_hostname='node2', host_ip='127.0.0.1',
hypervisor_version=0),
dict(id=3, local_gb=4096, memory_mb=4096, vcpus=4,
disk_available_least=3072, free_ram_mb=3072, vcpus_used=1,
free_disk_mb=3072, local_gb_used=0, updated_at=None,
service=dict(host='host3', disabled=False),
hypervisor_hostname='node3', host_ip='127.0.0.1'),
hypervisor_hostname='node3', host_ip='127.0.0.1',
hypervisor_version=0),
dict(id=4, local_gb=8192, memory_mb=8192, vcpus=8,
disk_available_least=8192, free_ram_mb=8192, vcpus_used=0,
free_disk_mb=8192, local_gb_used=0, updated_at=None,
service=dict(host='host4', disabled=False),
hypervisor_hostname='node4', host_ip='127.0.0.1'),
hypervisor_hostname='node4', host_ip='127.0.0.1',
hypervisor_version=0),
# Broken entry
dict(id=5, local_gb=1024, memory_mb=1024, vcpus=1, service=None),
]

View File

@@ -31,6 +31,7 @@ from nova.scheduler.filters import trusted_filter
from nova import servicegroup
from nova import test
from nova.tests.scheduler import fakes
from nova import utils
CONF = cfg.CONF
CONF.import_opt('my_ip', 'nova.netconf')
@@ -652,17 +653,19 @@ class HostFiltersTestCase(test.NoDBTestCase):
{'free_ram_mb': 1024, 'service': service})
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_passes_same_inst_props(self):
def test_image_properties_filter_passes_same_inst_props_and_version(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ImagePropertiesFilter']()
img_props = {'properties': {'_architecture': 'x86_64',
'hypervisor_type': 'kvm',
'vm_mode': 'hvm'}}
'vm_mode': 'hvm',
'hypervisor_version_requires': '>=6.0,<6.2'
}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'supported_instances': [
('x86_64', 'kvm', 'hvm')]}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_fails_different_inst_props(self):
@@ -672,10 +675,25 @@ class HostFiltersTestCase(test.NoDBTestCase):
'hypervisor_type': 'qemu',
'vm_mode': 'hvm'}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'supported_instances': [
('x86_64', 'kvm', 'hvm')]}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_fails_different_hyper_version(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ImagePropertiesFilter']()
img_props = {'properties': {'architecture': 'x86_64',
'hypervisor_type': 'kvm',
'vm_mode': 'hvm',
'hypervisor_version_requires': '>=6.2'}}
filter_properties = {'request_spec': {'image': img_props}}
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'enabled': True,
'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_passes_partial_inst_props(self):
@@ -684,10 +702,10 @@ class HostFiltersTestCase(test.NoDBTestCase):
img_props = {'properties': {'architecture': 'x86_64',
'vm_mode': 'hvm'}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'supported_instances': [
('x86_64', 'kvm', 'hvm')]}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_fails_partial_inst_props(self):
@@ -696,20 +714,20 @@ class HostFiltersTestCase(test.NoDBTestCase):
img_props = {'properties': {'architecture': 'x86_64',
'vm_mode': 'hvm'}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'supported_instances': [
('x86_64', 'xen', 'xen')]}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'supported_instances': [('x86_64', 'xen', 'xen')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_passes_without_inst_props(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ImagePropertiesFilter']()
filter_properties = {'request_spec': {}}
capabilities = {'supported_instances': [
('x86_64', 'kvm', 'hvm')]}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_fails_without_host_props(self):
@@ -719,7 +737,37 @@ class HostFiltersTestCase(test.NoDBTestCase):
'hypervisor_type': 'kvm',
'vm_mode': 'hvm'}}
filter_properties = {'request_spec': {'image': img_props}}
host = fakes.FakeHostState('host1', 'node1', {})
hypervisor_version = utils.convert_version_to_int('6.0.0')
capabilities = {'enabled': True,
'hypervisor_version': hypervisor_version}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_passes_without_hyper_version(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ImagePropertiesFilter']()
img_props = {'properties': {'architecture': 'x86_64',
'hypervisor_type': 'kvm',
'vm_mode': 'hvm',
'hypervisor_version_requires': '>=6.0'}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'enabled': True,
'supported_instances': [('x86_64', 'kvm', 'hvm')]}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def test_image_properties_filter_fails_with_unsupported_hyper_ver(self):
self._stub_service_is_up(True)
filt_cls = self.class_map['ImagePropertiesFilter']()
img_props = {'properties': {'architecture': 'x86_64',
'hypervisor_type': 'kvm',
'vm_mode': 'hvm',
'hypervisor_version_requires': '>=6.0'}}
filter_properties = {'request_spec': {'image': img_props}}
capabilities = {'enabled': True,
'supported_instances': [('x86_64', 'kvm', 'hvm')],
'hypervisor_version': 5000}
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertFalse(filt_cls.host_passes(host, filter_properties))
def _do_test_compute_filter_extra_specs(self, ecaps, especs, passes):
@@ -786,8 +834,7 @@ class HostFiltersTestCase(test.NoDBTestCase):
filter_properties = {'context': self.context, 'instance_type':
{'memory_mb': 1024}}
host = fakes.FakeHostState('host1', 'node1',
capabilities)
host = fakes.FakeHostState('host1', 'node1', capabilities)
self.assertTrue(filt_cls.host_passes(host, filter_properties))
def _create_aggregate_with_host(self, name='fake_aggregate',

View File

@@ -25,6 +25,7 @@ from nova.scheduler import host_manager
from nova import test
from nova.tests import matchers
from nova.tests.scheduler import fakes
from nova import utils
class FakeFilterClass1(filters.BaseHostFilter):
@@ -435,12 +436,14 @@ class HostStateTestCase(test.NoDBTestCase):
dict(key='num_os_type_windoze', value='1'),
dict(key='io_workload', value='42'),
]
hyper_ver_int = utils.convert_version_to_int('6.0.0')
compute = dict(stats=stats, memory_mb=1, 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',
hypervisor_type='htype', hypervisor_version='1.1',
hypervisor_type='htype',
hypervisor_hostname='hostname', cpu_info='cpu_info',
supported_instances='{}')
supported_instances='{}',
hypervisor_version=hyper_ver_int)
host = host_manager.HostState("fakehost", "fakenode")
host.update_from_compute_node(compute)
@@ -459,10 +462,10 @@ class HostStateTestCase(test.NoDBTestCase):
self.assertEqual('127.0.0.1', host.host_ip)
self.assertEqual('htype', host.hypervisor_type)
self.assertEqual('1.1', host.hypervisor_version)
self.assertEqual('hostname', host.hypervisor_hostname)
self.assertEqual('cpu_info', host.cpu_info)
self.assertEqual({}, host.supported_instances)
self.assertEqual(hyper_ver_int, host.hypervisor_version)
def test_stat_consumption_from_compute_node_non_pci(self):
stats = [
@@ -477,13 +480,16 @@ class HostStateTestCase(test.NoDBTestCase):
dict(key='num_os_type_windoze', value='1'),
dict(key='io_workload', value='42'),
]
hyper_ver_int = utils.convert_version_to_int('6.0.0')
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')
updated_at=None, host_ip='127.0.0.1',
hypervisor_version=hyper_ver_int)
host = host_manager.HostState("fakehost", "fakenode")
host.update_from_compute_node(compute)
self.assertEqual(None, host.pci_stats)
self.assertEqual(hyper_ver_int, host.hypervisor_version)
def test_stat_consumption_from_instance(self):
host = host_manager.HostState("fakehost", "fakenode")

View File

@@ -925,3 +925,19 @@ class GetImageFromSystemMetadataTestCase(test.NoDBTestCase):
# Verify that the foo1 key has not been inherited
self.assertNotIn("foo1", image)
class VersionTestCase(test.TestCase):
def test_convert_version_to_int(self):
self.assertEqual(utils.convert_version_to_int('6.2.0'), 6002000)
self.assertEqual(utils.convert_version_to_int((6, 4, 3)), 6004003)
self.assertEqual(utils.convert_version_to_int((5, )), 5)
self.assertRaises(exception.NovaException,
utils.convert_version_to_int, '5a.6b')
def test_convert_version_to_string(self):
self.assertEqual(utils.convert_version_to_str(6007000), '6.7.0')
self.assertEqual(utils.convert_version_to_str(4), '4')
def test_convert_version_to_tuple(self):
self.assertEqual(utils.convert_version_to_tuple('6.7.0'), (6, 7, 0))

View File

@@ -1027,7 +1027,28 @@ def is_none_string(val):
def convert_version_to_int(version):
return version[0] * 1000000 + version[1] * 1000 + version[2]
try:
if type(version) == str:
version = convert_version_to_tuple(version)
if type(version) == tuple:
return reduce(lambda x, y: (x * 1000) + y, version)
except Exception:
raise exception.NovaException(message="Hypervisor version invalid.")
def convert_version_to_str(version_int):
version_numbers = []
factor = 1000
while version_int != 0:
version_number = version_int - (version_int // factor * factor)
version_numbers.insert(0, str(version_number))
version_int = version_int / factor
return reduce(lambda x, y: "%s.%s" % (x, y), version_numbers)
def convert_version_to_tuple(version_str):
return tuple(int(part) for part in version_str.split('.'))
def is_neutron():

View File

@@ -743,9 +743,7 @@ class XenAPISession(object):
product_version_str = software_version.get('platform_version',
'0.0.0')
product_brand = software_version.get('product_brand')
product_version = tuple(int(part) for part in
product_version_str.split('.'))
product_version = utils.convert_version_to_tuple(product_version_str)
return product_version, product_brand