Merge "Teach HostAPI about cells"
This commit is contained in:
commit
b87bd4b416
|
@ -108,7 +108,8 @@ class EvacuateController(wsgi.Controller):
|
|||
if host is not None:
|
||||
try:
|
||||
self.host_api.service_get_by_compute_host(context, host)
|
||||
except exception.ComputeHostNotFound:
|
||||
except (exception.ComputeHostNotFound,
|
||||
exception.HostMappingNotFound):
|
||||
msg = _("Compute host %s not found.") % host
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ from nova.api.openstack import extensions
|
|||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova import compute
|
||||
from nova import context as nova_context
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova.policies import hosts as hosts_policies
|
||||
|
@ -85,7 +86,7 @@ class HostController(wsgi.Controller):
|
|||
if zone:
|
||||
filters['availability_zone'] = zone
|
||||
services = self.api.service_get_all(context, filters=filters,
|
||||
set_zones=True)
|
||||
set_zones=True, all_cells=True)
|
||||
hosts = []
|
||||
api_services = ('nova-osapi_compute', 'nova-ec2', 'nova-metadata')
|
||||
for service in services:
|
||||
|
@ -143,7 +144,7 @@ class HostController(wsgi.Controller):
|
|||
result = self.api.set_host_maintenance(context, host_name, mode)
|
||||
except NotImplementedError:
|
||||
common.raise_feature_not_supported()
|
||||
except exception.HostNotFound as e:
|
||||
except (exception.HostNotFound, exception.HostMappingNotFound) as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.ComputeServiceUnavailable as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
@ -161,11 +162,10 @@ class HostController(wsgi.Controller):
|
|||
else:
|
||||
LOG.info("Disabling host %s.", host_name)
|
||||
try:
|
||||
result = self.api.set_host_enabled(context, host_name=host_name,
|
||||
enabled=enabled)
|
||||
result = self.api.set_host_enabled(context, host_name, enabled)
|
||||
except NotImplementedError:
|
||||
common.raise_feature_not_supported()
|
||||
except exception.HostNotFound as e:
|
||||
except (exception.HostNotFound, exception.HostMappingNotFound) as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.ComputeServiceUnavailable as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
@ -178,11 +178,10 @@ class HostController(wsgi.Controller):
|
|||
context = req.environ['nova.context']
|
||||
context.can(hosts_policies.BASE_POLICY_NAME)
|
||||
try:
|
||||
result = self.api.host_power_action(context, host_name=host_name,
|
||||
action=action)
|
||||
result = self.api.host_power_action(context, host_name, action)
|
||||
except NotImplementedError:
|
||||
common.raise_feature_not_supported()
|
||||
except exception.HostNotFound as e:
|
||||
except (exception.HostNotFound, exception.HostMappingNotFound) as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.ComputeServiceUnavailable as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
@ -265,12 +264,15 @@ class HostController(wsgi.Controller):
|
|||
context.can(hosts_policies.BASE_POLICY_NAME)
|
||||
host_name = id
|
||||
try:
|
||||
mapping = objects.HostMapping.get_by_host(context, host_name)
|
||||
nova_context.set_target_cell(context, mapping.cell_mapping)
|
||||
compute_node = (
|
||||
objects.ComputeNode.get_first_node_by_host_for_old_compat(
|
||||
context, host_name))
|
||||
except exception.ComputeHostNotFound as e:
|
||||
instances = self.api.instance_get_all_by_host(context, host_name)
|
||||
except (exception.ComputeHostNotFound,
|
||||
exception.HostMappingNotFound) as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
instances = self.api.instance_get_all_by_host(context, host_name)
|
||||
resources = [self._get_total_resources(host_name, compute_node)]
|
||||
resources.append(self._get_used_now_resources(host_name,
|
||||
compute_node))
|
||||
|
|
|
@ -211,7 +211,8 @@ class HypervisorsController(wsgi.Controller):
|
|||
uptime = self.host_api.get_host_uptime(context, host)
|
||||
except NotImplementedError:
|
||||
common.raise_feature_not_supported()
|
||||
except exception.ComputeServiceUnavailable as e:
|
||||
except (exception.ComputeServiceUnavailable,
|
||||
exception.HostMappingNotFound) as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
service = self.host_api.service_get_by_compute_host(context, host)
|
||||
|
@ -246,10 +247,13 @@ class HypervisorsController(wsgi.Controller):
|
|||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
hypervisors = []
|
||||
for compute_node in compute_nodes:
|
||||
instances = self.host_api.instance_get_all_by_host(context,
|
||||
try:
|
||||
instances = self.host_api.instance_get_all_by_host(context,
|
||||
compute_node.host)
|
||||
service = self.host_api.service_get_by_compute_host(
|
||||
context, compute_node.host)
|
||||
service = self.host_api.service_get_by_compute_host(
|
||||
context, compute_node.host)
|
||||
except exception.HostMappingNotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
||||
hyp = self._view_hypervisor(compute_node, service, False, req,
|
||||
instances)
|
||||
hypervisors.append(hyp)
|
||||
|
|
|
@ -47,7 +47,8 @@ class ServiceController(wsgi.Controller):
|
|||
|
||||
_services = [
|
||||
s
|
||||
for s in self.host_api.service_get_all(context, set_zones=True)
|
||||
for s in self.host_api.service_get_all(context, set_zones=True,
|
||||
all_cells=True)
|
||||
if s['binary'] not in api_services
|
||||
]
|
||||
|
||||
|
@ -150,7 +151,8 @@ class ServiceController(wsgi.Controller):
|
|||
"""Do the actual PUT/update"""
|
||||
try:
|
||||
self.host_api.service_update(context, host, binary, payload)
|
||||
except exception.HostBinaryNotFound as exc:
|
||||
except (exception.HostBinaryNotFound,
|
||||
exception.HostMappingNotFound) as exc:
|
||||
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
|
||||
|
||||
def _perform_action(self, req, id, body, actions):
|
||||
|
@ -193,6 +195,9 @@ class ServiceController(wsgi.Controller):
|
|||
except exception.ServiceNotFound:
|
||||
explanation = _("Service %s not found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||
except exception.ServiceNotUnique:
|
||||
explanation = _("Service id %s refers to multiple services.") % id
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
|
||||
@extensions.expected_errors(())
|
||||
def index(self, req):
|
||||
|
|
|
@ -4283,6 +4283,22 @@ class API(base.Base):
|
|||
return host_statuses
|
||||
|
||||
|
||||
def target_host_cell(fn):
|
||||
"""Target a host-based function to a cell.
|
||||
|
||||
Expects to wrap a function of signature:
|
||||
|
||||
func(self, context, host, ...)
|
||||
"""
|
||||
|
||||
@functools.wraps(fn)
|
||||
def targeted(self, context, host, *args, **kwargs):
|
||||
mapping = objects.HostMapping.get_by_host(context, host)
|
||||
nova_context.set_target_cell(context, mapping.cell_mapping)
|
||||
return fn(self, context, host, *args, **kwargs)
|
||||
return targeted
|
||||
|
||||
|
||||
class HostAPI(base.Base):
|
||||
"""Sub-set of the Compute Manager API for managing host operations."""
|
||||
|
||||
|
@ -4301,6 +4317,7 @@ class HostAPI(base.Base):
|
|||
return service['host']
|
||||
|
||||
@wrap_exception()
|
||||
@target_host_cell
|
||||
def set_host_enabled(self, context, host_name, enabled):
|
||||
"""Sets the specified host's ability to accept new instances."""
|
||||
host_name = self._assert_host_exists(context, host_name)
|
||||
|
@ -4315,6 +4332,7 @@ class HostAPI(base.Base):
|
|||
payload)
|
||||
return result
|
||||
|
||||
@target_host_cell
|
||||
def get_host_uptime(self, context, host_name):
|
||||
"""Returns the result of calling "uptime" on the target host."""
|
||||
host_name = self._assert_host_exists(context, host_name,
|
||||
|
@ -4322,6 +4340,7 @@ class HostAPI(base.Base):
|
|||
return self.rpcapi.get_host_uptime(context, host=host_name)
|
||||
|
||||
@wrap_exception()
|
||||
@target_host_cell
|
||||
def host_power_action(self, context, host_name, action):
|
||||
"""Reboots, shuts down or powers up the host."""
|
||||
host_name = self._assert_host_exists(context, host_name)
|
||||
|
@ -4337,6 +4356,7 @@ class HostAPI(base.Base):
|
|||
return result
|
||||
|
||||
@wrap_exception()
|
||||
@target_host_cell
|
||||
def set_host_maintenance(self, context, host_name, mode):
|
||||
"""Start/Stop host maintenance window. On start, it triggers
|
||||
guest VMs evacuation.
|
||||
|
@ -4393,10 +4413,51 @@ class HostAPI(base.Base):
|
|||
ret_services.append(service)
|
||||
return ret_services
|
||||
|
||||
def _find_service(self, context, service_id):
|
||||
"""Find a service by id by searching all cells.
|
||||
|
||||
If one matching service is found, return it. If none or multiple
|
||||
are found, raise an exception.
|
||||
|
||||
:param context: A context.RequestContext
|
||||
:param service_id: The DB ID of the service to find
|
||||
:returns: An objects.Service
|
||||
:raises: ServiceNotUnique if multiple matches are found
|
||||
:raises: ServiceNotFound if no matches are found
|
||||
"""
|
||||
|
||||
load_cells()
|
||||
# NOTE(danms): Unfortunately this API exposes database identifiers
|
||||
# which means we really can't do something efficient here
|
||||
service = None
|
||||
found_in_cell = None
|
||||
for cell in CELLS:
|
||||
# NOTE(danms): Services can be in cell0, so don't skip it here
|
||||
try:
|
||||
with nova_context.target_cell(context, cell):
|
||||
cell_service = objects.Service.get_by_id(context,
|
||||
service_id)
|
||||
except exception.ServiceNotFound:
|
||||
# NOTE(danms): Keep looking in other cells
|
||||
continue
|
||||
if service and cell_service:
|
||||
raise exception.ServiceNotUnique()
|
||||
service = cell_service
|
||||
found_in_cell = cell
|
||||
|
||||
if service:
|
||||
# NOTE(danms): Set the cell on the context so it remains
|
||||
# when we return to our caller
|
||||
nova_context.set_target_cell(context, found_in_cell)
|
||||
return service
|
||||
else:
|
||||
raise exception.ServiceNotFound(service_id=service_id)
|
||||
|
||||
def service_get_by_id(self, context, service_id):
|
||||
"""Get service entry for the given service id."""
|
||||
return objects.Service.get_by_id(context, service_id)
|
||||
return self._find_service(context, service_id)
|
||||
|
||||
@target_host_cell
|
||||
def service_get_by_compute_host(self, context, host_name):
|
||||
"""Get service entry for the given compute hostname."""
|
||||
return objects.Service.get_by_compute_host(context, host_name)
|
||||
|
@ -4408,6 +4469,7 @@ class HostAPI(base.Base):
|
|||
service.save()
|
||||
return service
|
||||
|
||||
@target_host_cell
|
||||
def service_update(self, context, host_name, binary, params_to_update):
|
||||
"""Enable / Disable a service.
|
||||
|
||||
|
@ -4419,12 +4481,14 @@ class HostAPI(base.Base):
|
|||
|
||||
def _service_delete(self, context, service_id):
|
||||
"""Performs the actual Service deletion operation."""
|
||||
objects.Service.get_by_id(context, service_id).destroy()
|
||||
service = self._find_service(context, service_id)
|
||||
service.destroy()
|
||||
|
||||
def service_delete(self, context, service_id):
|
||||
"""Deletes the specified service."""
|
||||
self._service_delete(context, service_id)
|
||||
|
||||
@target_host_cell
|
||||
def instance_get_all_by_host(self, context, host_name):
|
||||
"""Return all instances on the given host."""
|
||||
return objects.InstanceList.get_by_host(context, host_name)
|
||||
|
@ -4442,15 +4506,65 @@ class HostAPI(base.Base):
|
|||
|
||||
def compute_node_get(self, context, compute_id):
|
||||
"""Return compute node entry for particular integer ID."""
|
||||
return objects.ComputeNode.get_by_id(context, int(compute_id))
|
||||
load_cells()
|
||||
|
||||
# NOTE(danms): Unfortunately this API exposes database identifiers
|
||||
# which means we really can't do something efficient here
|
||||
for cell in CELLS:
|
||||
if cell.uuid == objects.CellMapping.CELL0_UUID:
|
||||
continue
|
||||
with nova_context.target_cell(context, cell):
|
||||
try:
|
||||
return objects.ComputeNode.get_by_id(context,
|
||||
int(compute_id))
|
||||
except exception.ComputeHostNotFound:
|
||||
# NOTE(danms): Keep looking in other cells
|
||||
continue
|
||||
|
||||
raise exception.ComputeHostNotFound(host=compute_id)
|
||||
|
||||
def compute_node_get_all(self, context, limit=None, marker=None):
|
||||
return objects.ComputeNodeList.get_by_pagination(
|
||||
context, limit=limit, marker=marker)
|
||||
load_cells()
|
||||
|
||||
computes = []
|
||||
for cell in CELLS:
|
||||
if cell.uuid == objects.CellMapping.CELL0_UUID:
|
||||
continue
|
||||
with nova_context.target_cell(context, cell):
|
||||
try:
|
||||
cell_computes = objects.ComputeNodeList.get_by_pagination(
|
||||
context, limit=limit, marker=marker)
|
||||
except exception.MarkerNotFound:
|
||||
# NOTE(danms): Keep looking through cells
|
||||
continue
|
||||
computes.extend(cell_computes)
|
||||
# NOTE(danms): We must have found the marker, so continue on
|
||||
# without one
|
||||
marker = None
|
||||
if limit:
|
||||
limit -= len(cell_computes)
|
||||
if limit <= 0:
|
||||
break
|
||||
|
||||
if marker is not None and len(computes) == 0:
|
||||
# NOTE(danms): If we did not find the marker in any cell,
|
||||
# mimic the db_api behavior here.
|
||||
raise exception.MarkerNotFound(marker=marker)
|
||||
|
||||
return objects.ComputeNodeList(objects=computes)
|
||||
|
||||
def compute_node_search_by_hypervisor(self, context, hypervisor_match):
|
||||
return objects.ComputeNodeList.get_by_hypervisor(context,
|
||||
hypervisor_match)
|
||||
load_cells()
|
||||
|
||||
computes = []
|
||||
for cell in CELLS:
|
||||
if cell.uuid == objects.CellMapping.CELL0_UUID:
|
||||
continue
|
||||
with nova_context.target_cell(context, cell):
|
||||
cell_computes = objects.ComputeNodeList.get_by_hypervisor(
|
||||
context, hypervisor_match)
|
||||
computes.extend(cell_computes)
|
||||
return objects.ComputeNodeList(objects=computes)
|
||||
|
||||
def compute_node_statistics(self, context):
|
||||
return self.db.compute_node_statistics(context)
|
||||
|
|
|
@ -431,6 +431,10 @@ class ServiceUnavailable(Invalid):
|
|||
msg_fmt = _("Service is unavailable at this time.")
|
||||
|
||||
|
||||
class ServiceNotUnique(Invalid):
|
||||
msg_fmt = _("More than one possible service found.")
|
||||
|
||||
|
||||
class ComputeResourcesUnavailable(ServiceUnavailable):
|
||||
msg_fmt = _("Insufficient compute resources: %(reason)s.")
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
from oslo_utils import fixture as utils_fixture
|
||||
|
||||
from nova.tests import fixtures
|
||||
from nova.tests.functional.notification_sample_tests \
|
||||
import notification_sample_base
|
||||
from nova.tests.unit.api.openstack.compute import test_services
|
||||
|
@ -29,6 +30,7 @@ class TestServiceUpdateNotificationSample(
|
|||
self.stub_out("nova.db.service_update",
|
||||
test_services.fake_service_update)
|
||||
self.useFixture(utils_fixture.TimeFixture(test_services.fake_utcnow()))
|
||||
self.useFixture(fixtures.SingleCellSimple())
|
||||
|
||||
def test_service_enable(self):
|
||||
body = {'host': 'host1',
|
||||
|
|
|
@ -47,6 +47,8 @@ def fake_compute_api_get(self, context, instance_id, **kwargs):
|
|||
def fake_service_get_by_compute_host(self, context, host):
|
||||
if host == 'bad-host':
|
||||
raise exception.ComputeHostNotFound(host=host)
|
||||
elif host == 'unmapped-host':
|
||||
raise exception.HostMappingNotFound(name=host)
|
||||
else:
|
||||
return {
|
||||
'host_name': host,
|
||||
|
@ -154,6 +156,12 @@ class EvacuateTestV21(test.NoDBTestCase):
|
|||
'onSharedStorage': 'False',
|
||||
'adminPass': 'MyNewPass'})
|
||||
|
||||
def test_evacuate_instance_with_unmapped_target(self):
|
||||
self._check_evacuate_failure(webob.exc.HTTPNotFound,
|
||||
{'host': 'unmapped-host',
|
||||
'onSharedStorage': 'False',
|
||||
'adminPass': 'MyNewPass'})
|
||||
|
||||
def test_evacuate_instance_with_target(self):
|
||||
admin_pass = 'MyNewPass'
|
||||
res = self._get_evacuate_response({'host': 'my-host',
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import testtools
|
||||
import webob.exc
|
||||
|
||||
|
@ -23,6 +24,7 @@ from nova import context as context_maker
|
|||
from nova import db
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests import fixtures
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit import fake_hosts
|
||||
from nova.tests import uuidsentinel
|
||||
|
@ -158,6 +160,7 @@ class HostTestCaseV21(test.TestCase):
|
|||
self.controller = self.Controller()
|
||||
self.hosts_api = self.controller.api
|
||||
self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
|
||||
self.useFixture(fixtures.SingleCellSimple())
|
||||
|
||||
self._setup_stubs()
|
||||
|
||||
|
@ -369,6 +372,14 @@ class HostTestCaseV21(test.TestCase):
|
|||
db.instance_destroy(ctxt, i_ref1['uuid'])
|
||||
db.instance_destroy(ctxt, i_ref2['uuid'])
|
||||
|
||||
def test_show_late_host_mapping_gone(self):
|
||||
s_ref = self._create_compute_service()
|
||||
with mock.patch.object(self.controller.api,
|
||||
'instance_get_all_by_host') as m:
|
||||
m.side_effect = exception.HostMappingNotFound
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.show, self.req, s_ref['host'])
|
||||
|
||||
def test_list_hosts_with_zone(self):
|
||||
result = self.controller.index(FakeRequestWithNovaZone())
|
||||
self.assertIn('hosts', result)
|
||||
|
|
|
@ -447,6 +447,17 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
|||
mock_get_uptime.assert_called_once_with(
|
||||
mock.ANY, self.TEST_HYPERS_OBJ[0].host)
|
||||
|
||||
def test_uptime_hypervisor_not_mapped(self):
|
||||
with mock.patch.object(self.controller.host_api, 'get_host_uptime',
|
||||
side_effect=exception.HostMappingNotFound(name='dummy')
|
||||
) as mock_get_uptime:
|
||||
req = self._get_request(True)
|
||||
self.assertRaises(exc.HTTPBadRequest,
|
||||
self.controller.uptime, req,
|
||||
self.TEST_HYPERS_OBJ[0].id)
|
||||
mock_get_uptime.assert_called_once_with(
|
||||
mock.ANY, self.TEST_HYPERS_OBJ[0].host)
|
||||
|
||||
def test_search(self):
|
||||
req = self._get_request(True)
|
||||
result = self.controller.search(req, 'hyper')
|
||||
|
@ -488,6 +499,14 @@ class HypervisorsTestV21(test.NoDBTestCase):
|
|||
del server['name']
|
||||
self.assertEqual(dict(hypervisors=expected_dict), result)
|
||||
|
||||
def test_servers_not_mapped(self):
|
||||
req = self._get_request(True)
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'instance_get_all_by_host') as m:
|
||||
m.side_effect = exception.HostMappingNotFound
|
||||
self.assertRaises(exc.HTTPNotFound,
|
||||
self.controller.servers, req, 'hyper')
|
||||
|
||||
def test_servers_non_id(self):
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'compute_node_search_by_hypervisor',
|
||||
|
|
|
@ -33,6 +33,7 @@ from nova import exception
|
|||
from nova import objects
|
||||
from nova.servicegroup.drivers import db as db_driver
|
||||
from nova import test
|
||||
from nova.tests import fixtures
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit.objects import test_service
|
||||
|
||||
|
@ -130,7 +131,8 @@ class FakeRequestWithHostService(FakeRequest):
|
|||
|
||||
|
||||
def fake_service_get_all(services):
|
||||
def service_get_all(context, filters=None, set_zones=False):
|
||||
def service_get_all(context, filters=None, set_zones=False,
|
||||
all_cells=False):
|
||||
if set_zones or 'availability_zone' in filters:
|
||||
return availability_zones.set_availability_zones(context,
|
||||
services)
|
||||
|
@ -210,6 +212,7 @@ class ServicesTestV21(test.TestCase):
|
|||
fake_db_service_update(fake_services_list))
|
||||
|
||||
self.req = fakes.HTTPRequest.blank('')
|
||||
self.useFixture(fixtures.SingleCellSimple())
|
||||
|
||||
def _process_output(self, services, has_disabled=False, has_id=False):
|
||||
return services
|
||||
|
@ -487,6 +490,17 @@ class ServicesTestV21(test.TestCase):
|
|||
"enable",
|
||||
body=body)
|
||||
|
||||
def test_services_enable_with_unmapped_host(self):
|
||||
body = {'host': 'invalid', 'binary': 'nova-compute'}
|
||||
with mock.patch.object(self.controller.host_api,
|
||||
'service_update') as m:
|
||||
m.side_effect = exception.HostMappingNotFound
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update,
|
||||
self.req,
|
||||
"enable",
|
||||
body=body)
|
||||
|
||||
def test_services_enable_with_invalid_binary(self):
|
||||
body = {'host': 'host1', 'binary': 'invalid'}
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
|
@ -573,12 +587,19 @@ class ServicesTestV21(test.TestCase):
|
|||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.delete, self.req, 1234)
|
||||
|
||||
def test_services_delete_bad_request(self):
|
||||
def test_services_delete_invalid_id(self):
|
||||
self.ext_mgr.extensions['os-extended-services-delete'] = True
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, self.req, 'abc')
|
||||
|
||||
def test_services_delete_duplicate_service(self):
|
||||
with mock.patch.object(self.controller, 'host_api') as host_api:
|
||||
host_api.service_delete.side_effect = exception.ServiceNotUnique()
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, self.req, 1234)
|
||||
self.assertTrue(host_api.service_delete.called)
|
||||
|
||||
# This test is just to verify that the servicegroup API gets used when
|
||||
# calling the API
|
||||
@mock.patch.object(db_driver.DbDriver, 'is_up', side_effect=KeyError)
|
||||
|
|
|
@ -313,17 +313,47 @@ class ComputeHostAPITestCase(test.TestCase):
|
|||
|
||||
_do_test()
|
||||
|
||||
def test_service_delete(self):
|
||||
with test.nested(
|
||||
mock.patch.object(objects.Service, 'get_by_id',
|
||||
return_value=objects.Service()),
|
||||
mock.patch.object(objects.Service, 'destroy')
|
||||
) as (
|
||||
get_by_id, destroy
|
||||
):
|
||||
self.host_api.service_delete(self.ctxt, 1)
|
||||
get_by_id.assert_called_once_with(self.ctxt, 1)
|
||||
destroy.assert_called_once_with()
|
||||
@mock.patch('nova.context.set_target_cell')
|
||||
@mock.patch('nova.compute.api.load_cells')
|
||||
@mock.patch('nova.objects.Service.get_by_id')
|
||||
def test_service_delete(self, get_by_id, load_cells, set_target):
|
||||
compute_api.CELLS = [
|
||||
objects.CellMapping(),
|
||||
objects.CellMapping(),
|
||||
objects.CellMapping(),
|
||||
]
|
||||
|
||||
service = mock.MagicMock()
|
||||
get_by_id.side_effect = [exception.ServiceNotFound(service_id=1),
|
||||
service,
|
||||
exception.ServiceNotFound(service_id=1)]
|
||||
self.host_api.service_delete(self.ctxt, 1)
|
||||
get_by_id.assert_has_calls([mock.call(self.ctxt, 1),
|
||||
mock.call(self.ctxt, 1),
|
||||
mock.call(self.ctxt, 1)])
|
||||
service.destroy.assert_called_once_with()
|
||||
set_target.assert_called_once_with(self.ctxt, compute_api.CELLS[1])
|
||||
|
||||
@mock.patch('nova.context.set_target_cell')
|
||||
@mock.patch('nova.compute.api.load_cells')
|
||||
@mock.patch('nova.objects.Service.get_by_id')
|
||||
def test_service_delete_ambiguous(self, get_by_id, load_cells, set_target):
|
||||
compute_api.CELLS = [
|
||||
objects.CellMapping(),
|
||||
objects.CellMapping(),
|
||||
objects.CellMapping(),
|
||||
]
|
||||
|
||||
service1 = mock.MagicMock()
|
||||
service2 = mock.MagicMock()
|
||||
get_by_id.side_effect = [exception.ServiceNotFound(service_id=1),
|
||||
service1,
|
||||
service2]
|
||||
self.assertRaises(exception.ServiceNotUnique,
|
||||
self.host_api.service_delete, self.ctxt, 1)
|
||||
self.assertFalse(service1.destroy.called)
|
||||
self.assertFalse(service2.destroy.called)
|
||||
self.assertFalse(set_target.called)
|
||||
|
||||
def test_service_delete_compute_in_aggregate(self):
|
||||
compute = self.host_api.db.service_create(self.ctxt,
|
||||
|
@ -353,6 +383,10 @@ class ComputeHostAPICellsTestCase(ComputeHostAPITestCase):
|
|||
def test_service_get_all_cells(self):
|
||||
pass
|
||||
|
||||
@testtools.skip('cellsv1 does not use this')
|
||||
def test_service_delete_ambiguous(self):
|
||||
pass
|
||||
|
||||
def test_service_get_all_no_zones(self):
|
||||
services = [
|
||||
cells_utils.ServiceProxy(
|
||||
|
|
Loading…
Reference in New Issue