Merge "Move ComputeNode creation at init stage in ResourceTracker"

This commit is contained in:
Jenkins
2015-03-27 19:28:47 +00:00
committed by Gerrit Code Review
5 changed files with 253 additions and 170 deletions
+80 -57
View File
@@ -284,6 +284,64 @@ class ResourceTracker(object):
def disabled(self):
return self.compute_node is None
def _init_compute_node(self, context, resources):
"""Initialise the compute node if it does not already exist.
The resource tracker will be inoperable if compute_node
is not defined. The compute_node will remain undefined if
we fail to create it or if there is no associated service
registered.
If this method has to create a compute node it needs initial
values - these come from resources.
:param context: security context
:param resources: initial values
"""
# if there is already a compute node we don't
# need to do anything
if self.compute_node:
return
# TODO(pmurray): this lookup should be removed when the service_id
# field in the compute node goes away. At the moment it is deprecated
# but still a required field, so it has to be assigned below.
service = self._get_service(context)
if not service:
# no service record, disable resource
return
# now try to get the compute node record from the
# database. If we get one we are done.
self.compute_node = self._get_compute_node(context)
if self.compute_node:
return
# there was no local copy and none in the database
# so we need to create a new compute node. This needs
# to be initialised with resource values.
cn = {}
cn.update(resources)
# TODO(pmurray) service_id is deprecated but is still a required field.
# This should be removed when the field is changed.
cn['service_id'] = service.id
cn['host'] = self.host
# initialize load stats from existing instances:
self._write_ext_resources(cn)
# NOTE(pmurray): the stats field is stored as a json string. The
# json conversion will be done automatically by the ComputeNode object
# so this can be removed when using ComputeNode.
cn['stats'] = jsonutils.dumps(cn['stats'])
# pci_passthrough_devices may be in resources but are not
# stored in compute nodes
cn.pop('pci_passthrough_devices', None)
self.compute_node = self.conductor_api.compute_node_create(context, cn)
LOG.info(_LI('Compute_service record created for '
'%(host)s:%(node)s'),
{'host': self.host, 'node': self.nodename})
def _get_host_metrics(self, context, nodename):
"""Get the metrics from monitors and
notify information to message bus.
@@ -320,7 +378,7 @@ class ResourceTracker(object):
if not resources:
# The virt driver does not support this function
LOG.info(_LI("Virt driver does not support "
"'get_available_resource' Compute tracking is disabled."))
"'get_available_resource'. Compute tracking is disabled."))
self.compute_node = None
return
resources['host_ip'] = CONF.my_ip
@@ -341,10 +399,20 @@ class ResourceTracker(object):
self._report_hypervisor_resource_view(resources)
return self._update_available_resource(context, resources)
self._update_available_resource(context, resources)
@utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
def _update_available_resource(self, context, resources):
# initialise the compute node object, creating it
# if it does not already exist.
self._init_compute_node(context, resources)
# if we could not init the compute node the tracker will be
# disabled and we should quit now
if self.disabled:
return
if 'pci_passthrough_devices' in resources:
devs = []
for dev in jsonutils.loads(resources.pop(
@@ -395,49 +463,18 @@ class ResourceTracker(object):
metrics = self._get_host_metrics(context, self.nodename)
resources['metrics'] = jsonutils.dumps(metrics)
self._sync_compute_node(context, resources)
def _sync_compute_node(self, context, resources):
"""Create or update the compute node DB record."""
if not self.compute_node:
# we need a copy of the ComputeNode record:
service = self._get_service(context)
if not service:
# no service record, disable resource
return
# TODO(sbauza): Juno compute nodes are missing the host field and
# the Juno ResourceTracker does not set this field, even if
# the ComputeNode object can show it.
# Unfortunately, as we're not yet using ComputeNode.save(), we need
# to add this field in the resources dict until the RT is using
# the ComputeNode.save() method for populating the table.
# tl;dr: To be removed once RT is using ComputeNode.save()
resources['host'] = self.host
cn = self._get_compute_node(context)
if cn:
self.compute_node = cn
if self.pci_tracker:
self.pci_tracker.set_compute_node_id(cn['id'])
if not self.compute_node:
# Need to create the ComputeNode record:
resources['service_id'] = service.id
resources['host'] = self.host
self._create(context, resources)
if self.pci_tracker:
self.pci_tracker.set_compute_node_id(self.compute_node['id'])
LOG.info(_LI('Compute_service record created for '
'%(host)s:%(node)s'),
{'host': self.host, 'node': self.nodename})
else:
# just update the record:
# TODO(sbauza): Juno compute nodes are missing the host field and
# the Juno ResourceTracker does not set this field, even if
# the ComputeNode object can show it.
# Unfortunately, as we're not yet using ComputeNode.save(), we need
# to add this field in the resources dict until the RT is using
# the ComputeNode.save() method for populating the table.
# tl;dr: To be removed once RT is using ComputeNode.save()
resources['host'] = self.host
self._update(context, resources)
LOG.info(_LI('Compute_service record updated for '
'%(host)s:%(node)s'),
self._update(context, resources)
LOG.info(_LI('Compute_service record updated for %(host)s:%(node)s'),
{'host': self.host, 'node': self.nodename})
def _get_compute_node(self, context):
@@ -455,20 +492,6 @@ class ResourceTracker(object):
resources['stats'].update(self.stats)
self.ext_resources_handler.write_resources(resources)
def _create(self, context, values):
"""Create the compute node in the DB."""
# initialize load stats from existing instances:
self._write_ext_resources(values)
# NOTE(pmurray): the stats field is stored as a json string. The
# json conversion will be done automatically by the ComputeNode object
# so this can be removed when using ComputeNode.
values['stats'] = jsonutils.dumps(values['stats'])
self.compute_node = self.conductor_api.compute_node_create(context,
values)
# NOTE(sbauza): We don't want to miss the first creation event
self._update_resource_stats(context, values)
def _get_service(self, context):
try:
return objects.Service.get_by_compute_host(context, self.host)
-16
View File
@@ -266,22 +266,6 @@ class PciDevTracker(object):
for dev in devs:
self._free_device(dev)
def set_compute_node_id(self, node_id):
"""Set the compute node id that this object is tracking for.
In current resource tracker implementation, the
compute_node entry is created in the last step of
update_available_resoruces, thus we have to lazily set the
compute_node_id at that time.
"""
if self.node_id and self.node_id != node_id:
raise exception.PciTrackerInvalidNodeId(node_id=self.node_id,
new_node_id=node_id)
self.node_id = node_id
for dev in self.pci_devs:
dev.compute_node_id = node_id
def get_instance_pci_devs(inst, request_id=None):
"""Get the devices allocated to one or all requests for an instance.
@@ -540,7 +540,7 @@ class MissingComputeNodeTestCase(BaseTestCase):
def _fake_create_compute_node(self, context, values):
self.created = True
return self._create_compute_node()
return self._create_compute_node(values)
def _fake_service_get_by_compute_host(self, ctx, host):
# return a service with no joined compute
@@ -731,20 +731,6 @@ class SchedulerClientTrackerTestCase(BaseTrackerTestCase):
self.tracker.scheduler_client.update_resource_stats = mock.Mock(
side_effect=self._fake_compute_node_update)
def test_create_resource(self):
self.tracker._write_ext_resources = mock.Mock()
self.tracker.conductor_api.compute_node_create = mock.Mock(
return_value=dict(id=1))
values = {'stats': {}, 'foo': 'bar', 'baz_count': 0}
self.tracker._create(self.context, values)
expected = {'stats': '{}', 'foo': 'bar', 'baz_count': 0,
'id': 1}
self.tracker.scheduler_client.update_resource_stats.\
assert_called_once_with(self.context,
("fakehost", "fakenode"),
expected)
def test_update_resource(self):
self.tracker._write_ext_resources = mock.Mock()
values = {'stats': {}, 'foo': 'bar', 'baz_count': 0}
+172 -67
View File
@@ -353,26 +353,31 @@ class BaseTestCase(test.NoDBTestCase):
class TestUpdateAvailableResources(BaseTestCase):
def _update_available_resources(self):
# We test RT._sync_compute_node separately, since the complexity
# We test RT._update separately, since the complexity
# of the update_available_resource() function is high enough as
# it is, we just want to focus here on testing the resources
# parameter that update_available_resource() eventually passes
# to _sync_compute_node().
with mock.patch.object(self.rt, '_sync_compute_node') as sync_mock:
# to _update().
with mock.patch.object(self.rt, '_update') as update_mock:
self.rt.update_available_resource(mock.sentinel.ctx)
return sync_mock
return update_mock
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_no_instances_no_migrations_no_reserved(self, get_mock, migr_mock):
def test_no_instances_no_migrations_no_reserved(self, get_mock, migr_mock,
get_cn_mock, service_mock):
self.flags(reserved_host_disk_mb=0,
reserved_host_memory_mb=0)
self._setup_rt()
get_mock.return_value = []
migr_mock.return_value = []
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
vd = self.driver_mock
vd.get_available_resource.assert_called_once_with('fake-node')
@@ -381,10 +386,15 @@ class TestUpdateAvailableResources(BaseTestCase):
expected_attrs=[
'system_metadata',
'numa_topology'])
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
migr_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -404,23 +414,32 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 0
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_no_instances_no_migrations_reserved_disk_and_ram(
self, get_mock, migr_mock):
self, get_mock, migr_mock, get_cn_mock, service_mock):
self.flags(reserved_host_disk_mb=1024,
reserved_host_memory_mb=512)
self._setup_rt()
get_mock.return_value = []
migr_mock.return_value = []
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -440,22 +459,32 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 0
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_some_instances_no_migrations(self, get_mock, migr_mock):
def test_some_instances_no_migrations(self, get_mock, migr_mock,
get_cn_mock, service_mock):
self.flags(reserved_host_disk_mb=0,
reserved_host_memory_mb=0)
self._setup_rt()
get_mock.return_value = _INSTANCE_FIXTURES
migr_mock.return_value = []
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -470,8 +499,8 @@ class TestUpdateAvailableResources(BaseTestCase):
# NOTE(jaypipes): Due to the design of the ERT, which now is used
# track VCPUs, the actual used VCPUs isn't
# "written" to the resources dictionary that is
# passed to _sync_compute_node() like all the other
# resources are. Instead, _sync_compute_node()
# passed to _update() like all the other
# resources are. Instead, _update()
# calls the ERT's write_resources() method, which
# then queries each resource handler plugin for the
# changes in its resource usage and the plugin
@@ -486,18 +515,23 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 1 # One active instance
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_orphaned_instances_no_migrations(self, get_mock, migr_mock):
def test_orphaned_instances_no_migrations(self, get_mock, migr_mock,
get_cn_mock, service_mock):
self.flags(reserved_host_disk_mb=0,
reserved_host_memory_mb=0)
self._setup_rt()
get_mock.return_value = []
migr_mock.return_value = []
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
# Orphaned instances are those that the virt driver has on
# record as consuming resources on the compute node, but the
@@ -519,9 +553,14 @@ class TestUpdateAvailableResources(BaseTestCase):
vd = self.driver_mock
vd.get_per_instance_usage.return_value = orphaned_usages
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -543,14 +582,17 @@ class TestUpdateAvailableResources(BaseTestCase):
# as running VMs...
'running_vms': 0
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_no_instances_source_migration(
self, get_mock, get_inst_mock, migr_mock):
def test_no_instances_source_migration(self, get_mock, get_inst_mock,
migr_mock, get_cn_mock,
service_mock):
# We test the behavior of update_available_resource() when
# there is an active migration that involves this compute node
# as the source host not the destination host, and the resource
@@ -567,15 +609,22 @@ class TestUpdateAvailableResources(BaseTestCase):
get_mock.return_value = []
migr_obj = _MIGRATION_FIXTURES['source-only']
migr_mock.return_value = [migr_obj]
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
# Migration.instance property is accessed in the migration
# processing code, and this property calls
# objects.Instance.get_by_uuid, so we have the migration return
inst_uuid = migr_obj.instance_uuid
get_inst_mock.return_value = _MIGRATION_INSTANCE_FIXTURES[inst_uuid]
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -595,14 +644,16 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 0
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_no_instances_dest_migration(
self, get_mock, get_inst_mock, migr_mock):
def test_no_instances_dest_migration(self, get_mock, get_inst_mock,
migr_mock, get_cn_mock, service_mock):
# We test the behavior of update_available_resource() when
# there is an active migration that involves this compute node
# as the destination host not the source host, and the resource
@@ -621,10 +672,17 @@ class TestUpdateAvailableResources(BaseTestCase):
migr_mock.return_value = [migr_obj]
inst_uuid = migr_obj.instance_uuid
get_inst_mock.return_value = _MIGRATION_INSTANCE_FIXTURES[inst_uuid]
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
service_mock.return_value = _SERVICE_FIXTURE
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -644,14 +702,18 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 0
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.objects.MigrationList.get_in_progress_by_host_and_node')
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceList.get_by_host_and_node')
def test_some_instances_source_and_dest_migration(
self, get_mock, get_inst_mock, migr_mock):
def test_some_instances_source_and_dest_migration(self, get_mock,
get_inst_mock, migr_mock,
get_cn_mock,
service_mock):
# We test the behavior of update_available_resource() when
# there is an active migration that involves this compute node
# as the destination host AND the source host, and the resource
@@ -665,6 +727,7 @@ class TestUpdateAvailableResources(BaseTestCase):
migr_obj = _MIGRATION_FIXTURES['source-and-dest']
migr_mock.return_value = [migr_obj]
service_mock.return_value = _SERVICE_FIXTURE
inst_uuid = migr_obj.instance_uuid
# The resizing instance has already had its instance type
# changed to the *new* instance type (the bigger one, instance type 2)
@@ -672,10 +735,16 @@ class TestUpdateAvailableResources(BaseTestCase):
all_instances = _INSTANCE_FIXTURES + [resizing_instance]
get_mock.return_value = all_instances
get_inst_mock.return_value = resizing_instance
get_cn_mock.return_value = _COMPUTE_NODE_FIXTURES[0]
sync_mock = self._update_available_resources()
update_mock = self._update_available_resources()
get_cn_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
expected_resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -690,7 +759,7 @@ class TestUpdateAvailableResources(BaseTestCase):
'memory_mb_used': 512, # 128 exist + 256 new flav + 128 old flav
'pci_device_pools': [],
# See NOTE(jaypipes) above for reason why this isn't accurate until
# _sync_compute_node() is called.
# _update() is called.
'vcpus_used': 0,
'hypervisor_type': 'fake',
'local_gb_used': 7, # 1G existing, 5G new flav + 1 old flav
@@ -699,22 +768,65 @@ class TestUpdateAvailableResources(BaseTestCase):
'vcpus': 4,
'running_vms': 2
}
sync_mock.assert_called_once_with(mock.sentinel.ctx,
update_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
class TestSyncComputeNode(BaseTestCase):
class TestInitComputeNode(BaseTestCase):
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
def test_no_op_init_compute_node(self, get_mock):
self._setup_rt()
capi = self.cond_api_mock
service_mock = capi.service_get_by_compute_host
create_mock = capi.compute_node_create
resources = copy.deepcopy(_VIRT_DRIVER_AVAIL_RESOURCES)
compute_node = copy.deepcopy(_COMPUTE_NODE_FIXTURES[0])
self.rt.compute_node = compute_node
self.rt._init_compute_node(mock.sentinel.ctx, resources)
self.assertFalse(service_mock.called)
self.assertFalse(get_mock.called)
self.assertFalse(create_mock.called)
self.assertFalse(self.rt.disabled)
@mock.patch('nova.objects.Service.get_by_compute_host')
def test_no_found_service_disabled(self, service_mock):
self._setup_rt()
service_mock.side_effect = exc.NotFound
resources = copy.deepcopy(_VIRT_DRIVER_AVAIL_RESOURCES)
self.rt._init_compute_node(mock.sentinel.ctx, resources)
self.rt._sync_compute_node(mock.sentinel.ctx, mock.sentinel.resources)
self.assertTrue(self.rt.disabled)
self.assertIsNone(self.rt.compute_node)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
def test_compute_node_loaded(self, get_mock, service_mock):
self._setup_rt()
def fake_get_node(_ctx, host, node):
res = copy.deepcopy(_COMPUTE_NODE_FIXTURES[0])
return res
capi = self.cond_api_mock
service_mock.return_value = _SERVICE_FIXTURE
get_mock.side_effect = fake_get_node
create_mock = capi.compute_node_create
resources = copy.deepcopy(_VIRT_DRIVER_AVAIL_RESOURCES)
self.rt._init_compute_node(mock.sentinel.ctx, resources)
service_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host')
get_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host',
'fake-node')
self.assertFalse(create_mock.called)
self.assertFalse(self.rt.disabled)
@mock.patch('nova.objects.Service.get_by_compute_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
def test_compute_node_created_on_empty(self, get_mock, service_mock):
@@ -750,29 +862,27 @@ class TestSyncComputeNode(BaseTestCase):
'memory_mb': 512,
'current_workload': 0,
'vcpus': 4,
'running_vms': 0
'running_vms': 0,
'pci_passthrough_devices': '[]'
}
# We need to do this because _sync_compute_node() actually modifies
# We need to do this because _update() actually modifies
# the supplied dictionary :(
expected_resources = copy.deepcopy(resources)
# NOTE(pmurray): This will go away when the ComputeNode object is used
expected_resources['stats'] = '{}'
# NOTE(pmurray): no intial values are calculated before the initial
# creation. vcpus is derived from ERT resources, so this means its
# value will be 0
expected_resources['vcpus'] = 0
# NOTE(jaypipes): This will go away once
# detach-compute-node-from-service blueprint is done
expected_resources['service_id'] = 1
# NOTE(sbauza): ResourceTracker adds host field
expected_resources['host'] = 'fake-host'
# NOTE(jaypipes): The ERT adds a "stats" field, containing a dictionary
# of stuff that comes from the resource tracker's
# stats_class thing.
expected_resources['stats'] = '{}'
# pci_passthrough_devices should is not held in compute nodes
del expected_resources['pci_passthrough_devices']
# NOTE(jaypipes): This is unfortunately necessary to the design of the
# ERT. The only way to set the initial state of the
# total amount of resources available to a compute
# node is to call the ERT's reset_resources() method,
# and it sets its totals based on the supplied values.
self.rt.ext_resources_handler.reset_resources(resources,
self.rt.driver)
self.rt._sync_compute_node(mock.sentinel.ctx, resources)
self.rt._init_compute_node(mock.sentinel.ctx, resources)
self.assertFalse(self.rt.disabled)
service_mock.assert_called_once_with(mock.sentinel.ctx, 'fake-host')
@@ -780,16 +890,9 @@ class TestSyncComputeNode(BaseTestCase):
'fake-node')
create_node_mock.assert_called_once_with(mock.sentinel.ctx,
expected_resources)
# TODO(jaypipes): The update_resource_stats() scheduler method should
# actually take the compute node identifier as a
# parameter instead of looking in the resources
# parameter for an "id" field in order to identify the
# compute node.
expected_resources['id'] = 1
urs_mock = self.sched_client_mock.update_resource_stats
urs_mock.assert_called_once_with(mock.sentinel.ctx,
('fake-host', 'fake-node'),
expected_resources)
class TestUpdateComputeNode(BaseTestCase):
@mock.patch('nova.objects.Service.get_by_compute_host')
def test_existing_compute_node_updated_same_resources(self, service_mock):
@@ -803,6 +906,9 @@ class TestSyncComputeNode(BaseTestCase):
# are checking below to see that update_resource_stats() is not
# needlessly called when the resources don't actually change.
resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -823,20 +929,20 @@ class TestSyncComputeNode(BaseTestCase):
'running_vms': 0
}
orig_resources = copy.deepcopy(resources)
self.rt._sync_compute_node(mock.sentinel.ctx, resources)
self.rt._update(mock.sentinel.ctx, resources)
self.assertFalse(self.rt.disabled)
self.assertFalse(service_mock.called)
self.assertFalse(create_node_mock.called)
# The above call to _sync_compute_node() will populate the
# The above call to _update() will populate the
# RT.old_resources collection with the resources. Here, we check that
# if we call _sync_compute_node() again with the same resources, that
# if we call _update() again with the same resources, that
# the scheduler client won't be called again to update those
# (unchanged) resources for the compute node
self.sched_client_mock.reset_mock()
urs_mock = self.sched_client_mock.update_resource_stats
self.rt._sync_compute_node(mock.sentinel.ctx, orig_resources)
self.rt._update(mock.sentinel.ctx, orig_resources)
self.assertFalse(urs_mock.called)
@mock.patch('nova.objects.Service.get_by_compute_host')
@@ -850,8 +956,11 @@ class TestSyncComputeNode(BaseTestCase):
# Deliberately changing local_gb_used, vcpus_used, and memory_mb_used
# below to be different from the compute node fixture's base usages.
# We want to check that the code paths update the stored compute node
# usage records with what is supplied to _sync_compute_node().
# usage records with what is supplied to _update().
resources = {
# host is added in update_available_resources()
# before calling _update()
'host': 'fake-host',
'host_ip': 'fake-ip',
'numa_topology': None,
'metrics': '[]',
@@ -875,17 +984,13 @@ class TestSyncComputeNode(BaseTestCase):
expected_resources['id'] = 1
expected_resources['stats'] = '{}'
# TODO(sbauza): Remove this once RT is using ComputeNode.save() for
# updating the resources
expected_resources['host'] = 'fake-host'
self.rt.ext_resources_handler.reset_resources(resources,
self.rt.driver)
# This emulates the behavior that occurs in the
# RT.update_available_resource() method, which updates resource
# information in the ERT differently than all other resources.
self.rt.ext_resources_handler.update_from_instance(dict(vcpus=2))
self.rt._sync_compute_node(mock.sentinel.ctx, resources)
self.rt._update(mock.sentinel.ctx, resources)
self.assertFalse(self.rt.disabled)
self.assertFalse(service_mock.called)
-15
View File
@@ -307,21 +307,6 @@ class PciDevTrackerTestCase(test.NoDBTestCase):
self.assertEqual(len(self.tracker.pci_devs), 2)
self.assertEqual(self.destroy_called, 1)
def test_set_compute_node_id(self):
self.tracker = manager.PciDevTracker(self.fake_context)
fake_pci_devs = [copy.deepcopy(fake_pci), copy.deepcopy(fake_pci_1),
copy.deepcopy(fake_pci_2)]
self.tracker.set_hvdevs(fake_pci_devs)
self.tracker.set_compute_node_id(1)
self.assertEqual(self.tracker.node_id, 1)
self.assertEqual(self.tracker.pci_devs[0].compute_node_id, 1)
fake_pci_3 = dict(fake_pci, address='0000:00:00.4', vendor_id='v2')
fake_pci_devs = [copy.deepcopy(fake_pci), copy.deepcopy(fake_pci_1),
copy.deepcopy(fake_pci_3), copy.deepcopy(fake_pci_3)]
self.tracker.set_hvdevs(fake_pci_devs)
for dev in self.tracker.pci_devs:
self.assertEqual(dev.compute_node_id, 1)
@mock.patch('nova.objects.InstancePCIRequests.get_by_instance')
def test_clean_usage(self, mock_get):
inst_2 = copy.copy(self.inst)