Add support for select_destinations in Scheduler client

It was defined in the spec that the scheduler will provide a clear
interface for all scheduler API. Select_destinations() so needs
to be added in the client library.

Implements blueprint scheduler-lib

Change-Id: Ie5732baf9709cd0cb951eae4638910372c79e5f1
This commit is contained in:
Sylvain Bauza 2014-07-03 16:07:22 +02:00
parent f08b67f381
commit b16cd4548d
8 changed files with 193 additions and 47 deletions

View File

@ -44,8 +44,8 @@ from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova import quota
from nova.scheduler import client as scheduler_client
from nova.scheduler import driver as scheduler_driver
from nova.scheduler import rpcapi as scheduler_rpcapi
from nova.scheduler import utils as scheduler_utils
LOG = logging.getLogger(__name__)
@ -453,8 +453,8 @@ class ComputeTaskManager(base.Base):
def __init__(self):
super(ComputeTaskManager, self).__init__()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
self.image_api = image.API()
self.scheduler_client = scheduler_client.SchedulerClient()
@messaging.expected_exceptions(exception.NoValidHost,
exception.ComputeServiceUnavailable,
@ -504,7 +504,7 @@ class ComputeTaskManager(base.Base):
instance=instance)
try:
scheduler_utils.populate_retry(filter_properties, instance['uuid'])
hosts = self.scheduler_rpcapi.select_destinations(
hosts = self.scheduler_client.select_destinations(
context, request_spec, filter_properties)
host_state = hosts[0]
except exception.NoValidHost as ex:
@ -598,7 +598,7 @@ class ComputeTaskManager(base.Base):
# have a single instance.
scheduler_utils.populate_retry(filter_properties,
instances[0].uuid)
hosts = self.scheduler_rpcapi.select_destinations(context,
hosts = self.scheduler_client.select_destinations(context,
request_spec, filter_properties)
except Exception as exc:
for instance in instances:
@ -639,7 +639,7 @@ class ComputeTaskManager(base.Base):
*instances):
request_spec = scheduler_utils.build_request_spec(context, image,
instances)
hosts = self.scheduler_rpcapi.select_destinations(context,
hosts = self.scheduler_client.select_destinations(context,
request_spec, filter_properties)
return hosts
@ -720,7 +720,7 @@ class ComputeTaskManager(base.Base):
image_ref,
[instance])
try:
hosts = self.scheduler_rpcapi.select_destinations(context,
hosts = self.scheduler_client.select_destinations(context,
request_spec,
filter_properties)
host = hosts.pop(0)['host']

View File

@ -20,7 +20,7 @@ from nova import exception
from nova.i18n import _
from nova import image
from nova.openstack.common import log as logging
from nova.scheduler import rpcapi as scheduler_rpcapi
from nova.scheduler import client as scheduler_client
from nova.scheduler import utils as scheduler_utils
from nova import servicegroup
@ -48,7 +48,7 @@ class LiveMigrationTask(object):
self.migrate_data = None
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.servicegroup_api = servicegroup.API()
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
self.scheduler_client = scheduler_client.SchedulerClient()
self.image_api = image.API()
def execute(self):
@ -156,7 +156,7 @@ class LiveMigrationTask(object):
while host is None:
self._check_not_over_max_retries(attempted_hosts)
filter_properties = {'ignore_hosts': attempted_hosts}
host = self.scheduler_rpcapi.select_destinations(self.context,
host = self.scheduler_client.select_destinations(self.context,
request_spec, filter_properties)[0]['host']
try:
self._check_compatible_with_source_hypervisor(host)

View File

@ -0,0 +1,52 @@
# Copyright (c) 2014 Red Hat, Inc.
# 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.
import functools
from nova.openstack.common import importutils
class LazyLoader(object):
def __init__(self, klass, *args, **kwargs):
self.klass = klass
self.args = args
self.kwargs = kwargs
self.instance = None
def __getattr__(self, name):
return functools.partial(self.__run_method, name)
def __run_method(self, __name, *args, **kwargs):
if self.instance is None:
self.instance = self.klass(*self.args, **self.kwargs)
return getattr(self.instance, __name)(*args, **kwargs)
class SchedulerClient(object):
"""Client library for placing calls to the scheduler."""
def __init__(self):
self.queryclient = LazyLoader(importutils.import_class(
'nova.scheduler.client.query.SchedulerQueryClient'))
self.reportclient = LazyLoader(importutils.import_class(
'nova.scheduler.client.report.SchedulerReportClient'))
def select_destinations(self, context, request_spec, filter_properties):
return self.queryclient.select_destinations(
context, request_spec, filter_properties)
def update_resource_stats(self, context, name, stats):
self.reportclient.update_resource_stats(context, name, stats)

View File

@ -0,0 +1,34 @@
# Copyright (c) 2014 Red Hat, Inc.
# 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 rpcapi as scheduler_rpcapi
class SchedulerQueryClient(object):
"""Client class for querying to the scheduler."""
def __init__(self):
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
def select_destinations(self, context, request_spec, filter_properties):
"""Returns destinations(s) best suited for this request_spec and
filter_properties.
The result should be a list of dicts with 'host', 'nodename' and
'limits' as keys.
"""
return self.scheduler_rpcapi.select_destinations(
context, request_spec, filter_properties)

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova import conductor
from nova import exception
from nova.i18n import _LI
@ -21,8 +22,8 @@ from nova.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class SchedulerClient(object):
"""Client library for placing calls to the scheduler."""
class SchedulerReportClient(object):
"""Client class for updating the scheduler."""
def __init__(self):
self.conductor_api = conductor.API()

View File

@ -231,7 +231,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def test_find_destination_works(self):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
'_check_compatible_with_source_hypervisor')
@ -242,7 +242,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")
@ -255,7 +255,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance['image_ref'] = ''
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
'_check_compatible_with_source_hypervisor')
@ -263,7 +263,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
scheduler_utils.build_request_spec(self.context, None,
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")
@ -275,7 +275,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def _test_find_destination_retry_hypervisor_raises(self, error):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
'_check_compatible_with_source_hypervisor')
@ -286,13 +286,13 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")\
.AndRaise(error)
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host2'}])
self.task._check_compatible_with_source_hypervisor("host2")
@ -313,7 +313,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.flags(migrate_max_retries=1)
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
'_check_compatible_with_source_hypervisor')
@ -324,14 +324,14 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")
self.task._call_livem_checks_on_host("host1")\
.AndRaise(exception.Invalid)
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host2'}])
self.task._check_compatible_with_source_hypervisor("host2")
@ -344,7 +344,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.flags(migrate_max_retries=0)
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.task,
'_check_compatible_with_source_hypervisor')
@ -354,7 +354,7 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
[{'host': 'host1'}])
self.task._check_compatible_with_source_hypervisor("host1")\
@ -366,14 +366,14 @@ class LiveMigrationTaskTestCase(test.NoDBTestCase):
def test_find_destination_when_runs_out_of_hosts(self):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.task.scheduler_rpcapi,
self.mox.StubOutWithMock(self.task.scheduler_client,
'select_destinations')
compute_utils.get_image_metadata(self.context,
self.task.image_api, self.instance_image,
self.instance).AndReturn("image")
scheduler_utils.build_request_spec(self.context, mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn({})
self.task.scheduler_rpcapi.select_destinations(self.context,
self.task.scheduler_client.select_destinations(self.context,
mox.IgnoreArg(), mox.IgnoreArg()).AndRaise(
exception.NoValidHost(reason=""))

View File

@ -1157,7 +1157,7 @@ class _BaseTaskTestCase(object):
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(
self.conductor_manager.compute_rpcapi, 'prep_resize')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor_manager.scheduler_client,
'select_destinations')
inst = fake_instance.fake_db_instance(image_ref='image_ref')
inst_obj = objects.Instance._from_db_object(
@ -1176,7 +1176,7 @@ class _BaseTaskTestCase(object):
instance_type=flavor).AndReturn(request_spec)
hosts = [dict(host='host1', nodename=None, limits={})]
self.conductor_manager.scheduler_rpcapi.select_destinations(
self.conductor_manager.scheduler_client.select_destinations(
self.context, request_spec,
{'retry': {'num_attempts': 1, 'hosts': []}}).AndReturn(hosts)
@ -1216,7 +1216,7 @@ class _BaseTaskTestCase(object):
instance_properties = jsonutils.to_primitive(instances[0])
self.mox.StubOutWithMock(db, 'flavor_extra_specs_get')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor_manager.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
self.mox.StubOutWithMock(db,
@ -1227,7 +1227,7 @@ class _BaseTaskTestCase(object):
db.flavor_extra_specs_get(
self.context,
instance_type['flavorid']).AndReturn('fake-specs')
self.conductor_manager.scheduler_rpcapi.select_destinations(
self.conductor_manager.scheduler_client.select_destinations(
self.context, {'image': {'fake_data': 'should_pass_silently'},
'instance_properties': jsonutils.to_primitive(
instances[0]),
@ -1314,12 +1314,12 @@ class _BaseTaskTestCase(object):
exception = exc.NoValidHost(reason='fake-reason')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_driver, 'handle_schedule_error')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor_manager.scheduler_client,
'select_destinations')
scheduler_utils.build_request_spec(self.context, image,
mox.IgnoreArg()).AndReturn(spec)
self.conductor_manager.scheduler_rpcapi.select_destinations(
self.conductor_manager.scheduler_client.select_destinations(
self.context, spec,
{'retry': {'num_attempts': 1,
'hosts': []}}).AndRaise(exception)
@ -1495,7 +1495,7 @@ class _BaseTaskTestCase(object):
with contextlib.nested(
mock.patch.object(self.conductor_manager.compute_rpcapi,
'rebuild_instance'),
mock.patch.object(self.conductor_manager.scheduler_rpcapi,
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations')
) as (rebuild_mock, select_dest_mock):
self.conductor_manager.rebuild_instance(context=self.context,
@ -1519,7 +1519,7 @@ class _BaseTaskTestCase(object):
with contextlib.nested(
mock.patch.object(self.conductor_manager.compute_rpcapi,
'rebuild_instance'),
mock.patch.object(self.conductor_manager.scheduler_rpcapi,
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations',
return_value=[{'host': expected_host}]),
mock.patch('nova.scheduler.utils.build_request_spec',
@ -1548,7 +1548,7 @@ class _BaseTaskTestCase(object):
with contextlib.nested(
mock.patch.object(self.conductor_manager.compute_rpcapi,
'rebuild_instance'),
mock.patch.object(self.conductor_manager.scheduler_rpcapi,
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations',
side_effect=exc.NoValidHost(reason='')),
mock.patch('nova.scheduler.utils.build_request_spec',
@ -1724,7 +1724,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.conductor.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.conductor,
'_set_vm_state_and_notify')
@ -1740,7 +1740,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
exc_info = exc.NoValidHost(reason="")
self.conductor.scheduler_rpcapi.select_destinations(
self.conductor.scheduler_client.select_destinations(
self.context, request_spec,
filter_props).AndRaise(exc_info)
@ -1779,7 +1779,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.conductor.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.conductor,
'_set_vm_state_and_notify')
@ -1795,7 +1795,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
exc_info = exc.NoValidHost(reason="")
self.conductor.scheduler_rpcapi.select_destinations(
self.conductor.scheduler_client.select_destinations(
self.context, request_spec,
filter_props).AndRaise(exc_info)
@ -1834,7 +1834,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.mox.StubOutWithMock(compute_utils, 'get_image_metadata')
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(self.conductor.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(scheduler_utils,
'populate_filter_properties')
@ -1855,8 +1855,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
expected_filter_props = {'retry': {'num_attempts': 1,
'hosts': []},
'context': None}
self.conductor.scheduler_rpcapi.select_destinations(
self.conductor.scheduler_client.select_destinations(
self.context, request_spec,
expected_filter_props).AndReturn(hosts)
@ -1905,14 +1904,14 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
'instance_properties': instances[0]}
self.mox.StubOutWithMock(scheduler_utils, 'build_request_spec')
self.mox.StubOutWithMock(scheduler_driver, 'handle_schedule_error')
self.mox.StubOutWithMock(self.conductor_manager.scheduler_rpcapi,
self.mox.StubOutWithMock(self.conductor_manager.scheduler_client,
'select_destinations')
self.mox.StubOutWithMock(self.conductor_manager.compute_rpcapi,
'build_and_run_instance')
scheduler_utils.build_request_spec(self.context, image,
mox.IgnoreArg()).AndReturn(spec)
self.conductor_manager.scheduler_rpcapi.select_destinations(
self.conductor_manager.scheduler_client.select_destinations(
self.context, spec,
{'retry': {'num_attempts': 1, 'hosts': []}}).AndReturn(
[{'host': 'host1', 'nodename': 'node1', 'limits': []},
@ -1964,7 +1963,7 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
side_effect=exc.InstanceInfoCacheNotFound(
instance_uuid=instances[0].uuid)),
mock.patch.object(instances[1], 'refresh'),
mock.patch.object(self.conductor_manager.scheduler_rpcapi,
mock.patch.object(self.conductor_manager.scheduler_client,
'select_destinations', return_value=destinations),
mock.patch.object(self.conductor_manager.compute_rpcapi,
'build_and_run_instance')

View File

@ -19,19 +19,22 @@ from nova.conductor import api as conductor_api
from nova import context
from nova import exception
from nova.scheduler import client as scheduler_client
from nova.scheduler.client import query as scheduler_query_client
from nova.scheduler.client import report as scheduler_report_client
from nova.scheduler import rpcapi as scheduler_rpcapi
from nova import test
"""Tests for Scheduler Client."""
class SchedulerClientTestCase(test.TestCase):
class SchedulerReportClientTestCase(test.TestCase):
def setUp(self):
super(SchedulerClientTestCase, self).setUp()
super(SchedulerReportClientTestCase, self).setUp()
self.context = context.get_admin_context()
self.flags(use_local=True, group='conductor')
self.client = scheduler_client.SchedulerClient()
self.client = scheduler_report_client.SchedulerReportClient()
def test_constructor(self):
self.assertIsNotNone(self.client.conductor_api)
@ -51,3 +54,60 @@ class SchedulerClientTestCase(test.TestCase):
self.assertRaises(exception.ComputeHostNotCreated,
self.client.update_resource_stats,
self.context, ('fakehost', 'fakenode'), stats)
class SchedulerQueryClientTestCase(test.TestCase):
def setUp(self):
super(SchedulerQueryClientTestCase, self).setUp()
self.context = context.get_admin_context()
self.client = scheduler_query_client.SchedulerQueryClient()
def test_constructor(self):
self.assertIsNotNone(self.client.scheduler_rpcapi)
@mock.patch.object(scheduler_rpcapi.SchedulerAPI, 'select_destinations')
def test_select_destinations(self, mock_select_destinations):
self.client.select_destinations(
context=self.context,
request_spec='fake_request_spec',
filter_properties='fake_prop'
)
mock_select_destinations.assert_called_once_with(
self.context,
'fake_request_spec',
'fake_prop')
class SchedulerClientTestCase(test.TestCase):
def setUp(self):
super(SchedulerClientTestCase, self).setUp()
self.client = scheduler_client.SchedulerClient()
def test_constructor(self):
self.assertIsNotNone(self.client.queryclient)
self.assertIsNotNone(self.client.reportclient)
@mock.patch.object(scheduler_query_client.SchedulerQueryClient,
'select_destinations')
def test_select_destinations(self, mock_select_destinations):
self.assertIsNone(self.client.queryclient.instance)
self.client.select_destinations('ctxt', 'fake_spec', 'fake_prop')
self.assertIsNotNone(self.client.queryclient.instance)
mock_select_destinations.assert_called_once_with(
'ctxt', 'fake_spec', 'fake_prop')
@mock.patch.object(scheduler_report_client.SchedulerReportClient,
'update_resource_stats')
def test_update_resource_stats(self, mock_update_resource_stats):
self.assertIsNone(self.client.reportclient.instance)
self.client.update_resource_stats('ctxt', 'fake_name', 'fake_stats')
self.assertIsNotNone(self.client.reportclient.instance)
mock_update_resource_stats.assert_called_once_with(
'ctxt', 'fake_name', 'fake_stats')