Adding support for multiple hypervisor versions
This is to prevent instances with newer tools from being provisioned on older hypervisors. Added comparison of hypervisor version from the compute-node and image metadata in the image properties filter. In host scheduler image_prop_filter, the 'hypervisor_version_requires' image properties, if available, is compared to the host_state's hypervisor_version, if available. hypervisor_version_requires should be added on the image as metadata and should have a operator and version value. Examples : ">=6.0", ">6.0, <6.2", ""!=6.1" Partially Implements blueprint xen-support-for-hypervisor-versions Cell scheduler changes coming up in another commit. DocImpact Change-Id: I6fbf232adf9ac78ebb8ac2985739f163f4517d14
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user