nova/nova/tests/functional/db/test_compute_node.py

298 lines
11 KiB
Python

# 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 oslo_serialization import jsonutils
from oslo_utils.fixture import uuidsentinel
import nova.conf
from nova import context
from nova.db.main import api as db
from nova import objects
from nova.objects import compute_node
from nova.objects import fields as obj_fields
from nova import test
CONF = nova.conf.CONF
_HOSTNAME = 'fake-host'
_NODENAME = 'fake-node'
_VIRT_DRIVER_AVAIL_RESOURCES = {
'vcpus': 4,
'memory_mb': 512,
'local_gb': 6,
'vcpus_used': 0,
'memory_mb_used': 0,
'local_gb_used': 0,
'hypervisor_type': 'fake',
'hypervisor_version': 0,
'hypervisor_hostname': _NODENAME,
'cpu_info': '',
'numa_topology': None,
}
fake_compute_obj = objects.ComputeNode(
host=_HOSTNAME,
vcpus=_VIRT_DRIVER_AVAIL_RESOURCES['vcpus'],
memory_mb=_VIRT_DRIVER_AVAIL_RESOURCES['memory_mb'],
local_gb=_VIRT_DRIVER_AVAIL_RESOURCES['local_gb'],
vcpus_used=_VIRT_DRIVER_AVAIL_RESOURCES['vcpus_used'],
memory_mb_used=_VIRT_DRIVER_AVAIL_RESOURCES['memory_mb_used'],
local_gb_used=_VIRT_DRIVER_AVAIL_RESOURCES['local_gb_used'],
hypervisor_type='fake',
hypervisor_version=0,
hypervisor_hostname=_HOSTNAME,
free_ram_mb=(_VIRT_DRIVER_AVAIL_RESOURCES['memory_mb'] -
_VIRT_DRIVER_AVAIL_RESOURCES['memory_mb_used']),
free_disk_gb=(_VIRT_DRIVER_AVAIL_RESOURCES['local_gb'] -
_VIRT_DRIVER_AVAIL_RESOURCES['local_gb_used']),
current_workload=0,
running_vms=0,
cpu_info='{}',
disk_available_least=0,
host_ip='1.1.1.1',
supported_hv_specs=[
objects.HVSpec.from_list([
obj_fields.Architecture.I686,
obj_fields.HVType.KVM,
obj_fields.VMMode.HVM])
],
metrics=None,
pci_device_pools=None,
extra_resources=None,
stats={},
numa_topology=None,
cpu_allocation_ratio=16.0,
ram_allocation_ratio=1.5,
disk_allocation_ratio=1.0,
)
class ComputeNodeTestCase(test.TestCase):
def setUp(self):
super(ComputeNodeTestCase, self).setUp()
self.context = context.RequestContext('fake-user', 'fake-project')
def _create_zero_and_none_cn(self):
cn1 = fake_compute_obj.obj_clone()
cn1._context = self.context
cn1.create()
db.compute_node_update(self.context, cn1.id,
{'cpu_allocation_ratio': 0.0,
'disk_allocation_ratio': 0.0,
'ram_allocation_ratio': 0.0})
cn1_db = db.compute_node_get(self.context, cn1.id)
for x in ['cpu', 'disk', 'ram']:
self.assertEqual(0.0, cn1_db['%s_allocation_ratio' % x])
cn2 = fake_compute_obj.obj_clone()
cn2._context = self.context
cn2.host += '-alt'
cn2.create()
# We can't set a cn_obj.xxx_allocation_ratio to None,
# so we set ratio to None in db directly
db.compute_node_update(self.context, cn2.id,
{'cpu_allocation_ratio': None,
'disk_allocation_ratio': None,
'ram_allocation_ratio': None})
cn2_db = db.compute_node_get(self.context, cn2.id)
for x in ['cpu', 'disk', 'ram']:
self.assertIsNone(cn2_db['%s_allocation_ratio' % x])
def test_get_all_by_uuids(self):
cn1 = fake_compute_obj.obj_clone()
cn1._context = self.context
cn1.create()
cn2 = fake_compute_obj.obj_clone()
cn2._context = self.context
# Two compute nodes can't have the same tuple (host, node, deleted)
cn2.host = _HOSTNAME + '2'
cn2.create()
# A deleted compute node
cn3 = fake_compute_obj.obj_clone()
cn3._context = self.context
cn3.host = _HOSTNAME + '3'
cn3.create()
cn3.destroy()
cns = objects.ComputeNodeList.get_all_by_uuids(self.context, [])
self.assertEqual(0, len(cns))
# Ensure that asking for one compute node when there are multiple only
# returns the one we want.
cns = objects.ComputeNodeList.get_all_by_uuids(self.context,
[cn1.uuid])
self.assertEqual(1, len(cns))
cns = objects.ComputeNodeList.get_all_by_uuids(self.context,
[cn1.uuid, cn2.uuid])
self.assertEqual(2, len(cns))
# Ensure that asking for a non-existing UUID along with
# existing UUIDs doesn't limit the return of the existing
# compute nodes...
cns = objects.ComputeNodeList.get_all_by_uuids(self.context,
[cn1.uuid, cn2.uuid,
uuidsentinel.noexists])
self.assertEqual(2, len(cns))
# Ensure we don't get the deleted one, even if we ask for it
cns = objects.ComputeNodeList.get_all_by_uuids(self.context,
[cn1.uuid, cn2.uuid,
cn3.uuid])
self.assertEqual(2, len(cns))
def test_get_by_hypervisor_type(self):
cn1 = fake_compute_obj.obj_clone()
cn1._context = self.context
cn1.hypervisor_type = 'ironic'
cn1.create()
cn2 = fake_compute_obj.obj_clone()
cn2._context = self.context
cn2.hypervisor_type = 'libvirt'
cn2.host += '-alt'
cn2.create()
cns = objects.ComputeNodeList.get_by_hypervisor_type(self.context,
'ironic')
self.assertEqual(1, len(cns))
self.assertEqual(cn1.uuid, cns[0].uuid)
def test_numa_topology_online_migration_when_load(self):
"""Ensure legacy NUMA topology objects are reserialized to o.vo's."""
cn = fake_compute_obj.obj_clone()
cn._context = self.context
cn.create()
legacy_topology = jsonutils.dumps({
"cells": [
{
"id": 0,
"cpus": "0-3",
"mem": {"total": 512, "used": 256},
"cpu_usage": 2,
},
{
"id": 1,
"cpus": "4,5,6,7",
"mem": {"total": 512, "used": 0},
"cpu_usage": 0,
}
]
})
db.compute_node_update(
self.context, cn.id, {'numa_topology': legacy_topology})
cn_db = db.compute_node_get(self.context, cn.id)
self.assertEqual(legacy_topology, cn_db['numa_topology'])
self.assertNotIn('nova_object.name', cn_db['numa_topology'])
# trigger online migration
objects.ComputeNodeList.get_all(self.context)
cn_db = db.compute_node_get(self.context, cn.id)
self.assertNotEqual(legacy_topology, cn_db['numa_topology'])
self.assertIn('nova_object.name', cn_db['numa_topology'])
def test_ratio_online_migration_when_load(self):
# set cpu and disk, and leave ram unset(None)
self.flags(cpu_allocation_ratio=1.0)
self.flags(disk_allocation_ratio=2.0)
self._create_zero_and_none_cn()
# trigger online migration
objects.ComputeNodeList.get_all(self.context)
cns = db.compute_node_get_all(self.context)
for cn in cns:
# the cpu/disk ratio is refreshed to CONF.xxx_allocation_ratio
self.assertEqual(CONF.cpu_allocation_ratio,
cn['cpu_allocation_ratio'])
self.assertEqual(CONF.disk_allocation_ratio,
cn['disk_allocation_ratio'])
# the ram ratio is refreshed to CONF.initial_xxx_allocation_ratio
self.assertEqual(CONF.initial_ram_allocation_ratio,
cn['ram_allocation_ratio'])
def test_migrate_empty_ratio(self):
# we have 5 records to process, the last of which is deleted
for i in range(5):
cn = fake_compute_obj.obj_clone()
cn._context = self.context
cn.host += '-alt-%s' % i
cn.create()
db.compute_node_update(self.context, cn.id,
{'cpu_allocation_ratio': 0.0})
if i == 4:
cn.destroy()
# first only process 2
res = compute_node.migrate_empty_ratio(self.context, 2)
self.assertEqual(res, (2, 2))
# then process others - there should only be 2 found since one
# of the remaining compute nodes is deleted and gets filtered out
res = compute_node.migrate_empty_ratio(self.context, 999)
self.assertEqual(res, (2, 2))
def test_migrate_none_or_zero_ratio_with_none_ratio_conf(self):
cn1 = fake_compute_obj.obj_clone()
cn1._context = self.context
cn1.create()
db.compute_node_update(self.context, cn1.id,
{'cpu_allocation_ratio': 0.0,
'disk_allocation_ratio': 0.0,
'ram_allocation_ratio': 0.0})
self.flags(initial_cpu_allocation_ratio=32.0)
self.flags(initial_ram_allocation_ratio=8.0)
self.flags(initial_disk_allocation_ratio=2.0)
res = compute_node.migrate_empty_ratio(self.context, 1)
self.assertEqual(res, (1, 1))
# the ratio is refreshed to CONF.initial_xxx_allocation_ratio
# because CONF.xxx_allocation_ratio is None
cns = db.compute_node_get_all(self.context)
# the ratio is refreshed to CONF.xxx_allocation_ratio
for cn in cns:
for x in ['cpu', 'disk', 'ram']:
conf_key = 'initial_%s_allocation_ratio' % x
key = '%s_allocation_ratio' % x
self.assertEqual(getattr(CONF, conf_key), cn[key])
def test_migrate_none_or_zero_ratio_with_not_empty_ratio(self):
cn1 = fake_compute_obj.obj_clone()
cn1._context = self.context
cn1.create()
db.compute_node_update(self.context, cn1.id,
{'cpu_allocation_ratio': 32.0,
'ram_allocation_ratio': 4.0,
'disk_allocation_ratio': 3.0})
res = compute_node.migrate_empty_ratio(self.context, 1)
# the non-empty ratio will not be refreshed
self.assertEqual(res, (0, 0))
cns = db.compute_node_get_all(self.context)
for cn in cns:
self.assertEqual(32.0, cn['cpu_allocation_ratio'])
self.assertEqual(4.0, cn['ram_allocation_ratio'])
self.assertEqual(3.0, cn['disk_allocation_ratio'])