diff options
| author | Matthew Sherborne <msherborne@gmail.com> | 2013-03-15 13:46:12 +1000 |
|---|---|---|
| committer | Matthew Sherborne <msherborne@gmail.com> | 2013-04-23 22:04:01 +1000 |
| commit | 732bcdb9502aca5c6b38966bd1e53c79236300da (patch) | |
| tree | e7a5ab2c4fd2fad84c66d64548833f3bc2309236 | |
| parent | bfc3a3ccb2811cc8a96a15987528e6639ec029bf (diff) | |
Make os.services.update work with cells
In the nova.api.openstack.compute.contrib.services:
* Make update (enable/disable) service work
In nova.compute.api.api.HostAPI:
Add - service_update - Used by openstack api's services.py
In nova.compute.api.cells_api.HostAPI:
Add - service_update - Used by openstack api's services.py
In cells:
* Increase API version from 1.6 to 1.7
* Add service_update
Fixes bug #1150499
Change-Id: I5651bd5bc328692df82f4d9da27d390a8c95e03f
| -rw-r--r-- | nova/api/openstack/compute/contrib/services.py | 18 | ||||
| -rw-r--r-- | nova/cells/manager.py | 20 | ||||
| -rw-r--r-- | nova/cells/messaging.py | 35 | ||||
| -rw-r--r-- | nova/cells/rpcapi.py | 16 | ||||
| -rw-r--r-- | nova/compute/api.py | 9 | ||||
| -rw-r--r-- | nova/compute/cells_api.py | 13 | ||||
| -rw-r--r-- | nova/tests/cells/test_cells_manager.py | 20 | ||||
| -rw-r--r-- | nova/tests/cells/test_cells_messaging.py | 37 | ||||
| -rw-r--r-- | nova/tests/cells/test_cells_rpcapi.py | 14 | ||||
| -rw-r--r-- | nova/tests/compute/test_host_api.py | 39 |
10 files changed, 210 insertions, 11 deletions
diff --git a/nova/api/openstack/compute/contrib/services.py b/nova/api/openstack/compute/contrib/services.py index d948c8bc5..9952484f7 100644 --- a/nova/api/openstack/compute/contrib/services.py +++ b/nova/api/openstack/compute/contrib/services.py @@ -21,7 +21,6 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute -from nova import db from nova import exception from nova.openstack.common import timeutils from nova import utils @@ -121,7 +120,10 @@ class ServiceController(object): elif id == "disable": disabled = True else: - raise webob.exc.HTTPNotFound("Unknown action") + raise webob.exc.HTTPNotFound(_("Unknown action")) + + status = id + 'd' + try: host = body['host'] binary = body['binary'] @@ -129,15 +131,11 @@ class ServiceController(object): raise webob.exc.HTTPUnprocessableEntity() try: - svc = db.service_get_by_args(context, host, binary) - if not svc: - raise webob.exc.HTTPNotFound('Unknown service') + svc = self.host_api.service_update(context, host, binary, + {'disabled': disabled}) + except exception.ServiceNotFound as exc: + raise webob.exc.HTTPNotFound(_("Unknown service")) - db.service_update(context, svc['id'], {'disabled': disabled}) - except exception.ServiceNotFound: - raise webob.exc.HTTPNotFound("service not found") - - status = id + 'd' return {'service': {'host': host, 'binary': binary, 'status': status}} diff --git a/nova/cells/manager.py b/nova/cells/manager.py index 51d2f7cac..f536d270c 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -63,7 +63,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - RPC_API_VERSION = '1.6' + RPC_API_VERSION = '1.7' def __init__(self, *args, **kwargs): # Mostly for tests. @@ -250,6 +250,24 @@ class CellsManager(manager.Manager): cells_utils.add_cell_to_service(service, response.cell_name) return service + def service_update(self, ctxt, host_name, binary, params_to_update): + """ + Used to enable/disable a service. For compute services, setting to + disabled stops new builds arriving on that host. + + :param host_name: the name of the host machine that the service is + running + :param binary: The name of the executable that the service runs as + :param params_to_update: eg. {'disabled': True} + :returns: the service reference + """ + cell_name, host_name = cells_utils.split_cell_and_item(host_name) + response = self.msg_runner.service_update( + ctxt, cell_name, host_name, binary, params_to_update) + service = response.value_or_raise() + cells_utils.add_cell_to_service(service, response.cell_name) + return service + def proxy_rpc_to_manager(self, ctxt, topic, rpc_message, call, timeout): """Proxy an RPC message as-is to a manager.""" compute_topic = CONF.compute_topic diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index 02044ffd5..2e2fa735a 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -603,6 +603,7 @@ class _BaseMessageMethods(base.Base): self.compute_api = compute.API() self.compute_rpcapi = compute_rpcapi.ComputeAPI() self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() + self.host_api = compute.HostAPI() def task_log_get_all(self, message, task_name, period_beginning, period_ending, host, state): @@ -705,6 +706,20 @@ class _TargetedMessageMethods(_BaseMessageMethods): host_name) return jsonutils.to_primitive(service) + def service_update(self, message, host_name, binary, params_to_update): + """ + Used to enable/disable a service. For compute services, setting to + disabled stops new builds arriving on that host. + + :param host_name: the name of the host machine that the service is + running + :param binary: The name of the executable that the service runs as + :param params_to_update: eg. {'disabled': True} + """ + return jsonutils.to_primitive( + self.host_api.service_update(message.ctxt, host_name, binary, + params_to_update)) + def proxy_rpc_to_manager(self, message, host_name, rpc_message, topic, timeout): """Proxy RPC to the given compute topic.""" @@ -1168,6 +1183,26 @@ class MessageRunner(object): need_response=True) return message.process() + def service_update(self, ctxt, cell_name, host_name, binary, + params_to_update): + """ + Used to enable/disable a service. For compute services, setting to + disabled stops new builds arriving on that host. + + :param host_name: the name of the host machine that the service is + running + :param binary: The name of the executable that the service runs as + :param params_to_update: eg. {'disabled': True} + :returns: the update service object + """ + method_kwargs = dict(host_name=host_name, binary=binary, + params_to_update=params_to_update) + message = _TargetedMessage(self, ctxt, + 'service_update', + method_kwargs, 'down', cell_name, + need_response=True) + return message.process() + def proxy_rpc_to_manager(self, ctxt, cell_name, host_name, topic, rpc_message, call, timeout): method_kwargs = {'host_name': host_name, diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index ca6e23fb1..c8b7c680f 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -49,6 +49,7 @@ class CellsAPI(rpc_proxy.RpcProxy): 1.5 - Adds actions_get(), action_get_by_request_id(), and action_events_get() 1.6 - Adds consoleauth_delete_tokens() and validate_console_port() + 1.7 - Adds service_update() ''' BASE_RPC_API_VERSION = '1.0' @@ -179,6 +180,21 @@ class CellsAPI(rpc_proxy.RpcProxy): host_name=host_name), version='1.2') + def service_update(self, ctxt, host_name, binary, params_to_update): + """ + Used to enable/disable a service. For compute services, setting to + disabled stops new builds arriving on that host. + + :param host_name: the name of the host machine that the service is + running + :param binary: The name of the executable that the service runs as + :param params_to_update: eg. {'disabled': True} + """ + return self.call(ctxt, self.make_msg( + 'service_update', host_name=host_name, + binary=binary, params_to_update=params_to_update), + version='1.7') + def proxy_rpc_to_manager(self, ctxt, rpc_message, topic, call=False, timeout=None): """Proxy RPC to a compute manager. The host in the topic diff --git a/nova/compute/api.py b/nova/compute/api.py index 1b1a3c506..73d9a652c 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -41,6 +41,7 @@ from nova.compute import utils as compute_utils from nova.compute import vm_states from nova.consoleauth import rpcapi as consoleauth_rpcapi from nova import crypto +from nova import db from nova.db import base from nova import exception from nova import hooks @@ -2564,6 +2565,14 @@ class HostAPI(base.Base): """Get service entry for the given compute hostname.""" return self.db.service_get_by_compute_host(context, host_name) + def service_update(self, context, host_name, binary, params_to_update): + """ + Enable / Disable a service. + For compute services, this stops new builds and migrations going to + the host.""" + service = db.service_get_by_args(context, host_name, binary) + return db.service_update(context, service['id'], params_to_update) + def instance_get_all_by_host(self, context, host_name): """Return all instances on the given host.""" return self.db.instance_get_all_by_host(context, host_name) diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index f5ded45ec..9c0f72c97 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -603,6 +603,19 @@ class HostAPI(compute_api.HostAPI): return self.cells_rpcapi.service_get_by_compute_host(context, host_name) + def service_update(self, context, host_name, binary, params_to_update): + """ + Used to enable/disable a service. For compute services, setting to + disabled stops new builds arriving on that host. + + :param host_name: the name of the host machine that the service is + running + :param binary: The name of the executable that the service runs as + :param params_to_update: eg. {'disabled': True} + """ + return self.cells_rpcapi.service_update( + context, host_name, binary, params_to_update) + def instance_get_all_by_host(self, context, host_name): """Get all instances by host. Host might have a cell prepended to it, so we'll need to strip it out. We don't need to proxy diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index 74a7e9834..137d48ff6 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -294,6 +294,26 @@ class CellsManagerClassTestCase(test.TestCase): host_name=cell_and_host) self.assertEqual(expected_response, response) + def test_service_update(self): + fake_cell = 'fake-cell' + fake_response = messaging.Response( + fake_cell, FAKE_SERVICES[0], False) + expected_response = copy.deepcopy(FAKE_SERVICES[0]) + cells_utils.add_cell_to_service(expected_response, fake_cell) + cell_and_host = cells_utils.cell_with_item('fake-cell', 'fake-host') + params_to_update = {'disabled': True} + + self.mox.StubOutWithMock(self.msg_runner, 'service_update') + self.msg_runner.service_update(self.ctxt, + fake_cell, 'fake-host', 'nova-api', + params_to_update).AndReturn(fake_response) + self.mox.ReplayAll() + + response = self.cells_manager.service_update( + self.ctxt, host_name=cell_and_host, binary='nova-api', + params_to_update=params_to_update) + self.assertEqual(expected_response, response) + def test_proxy_rpc_to_manager(self): self.mox.StubOutWithMock(self.msg_runner, 'proxy_rpc_to_manager') diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index bed27085f..728856006 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -20,6 +20,7 @@ from oslo.config import cfg from nova.cells import messaging from nova.cells import utils as cells_utils from nova import context +from nova import db from nova import exception from nova.openstack.common import rpc from nova.openstack.common import timeutils @@ -745,6 +746,42 @@ class CellsTargetedMethodsTestCase(test.TestCase): result = response.value_or_raise() self.assertEqual('fake-service', result) + def test_service_update(self): + binary = 'nova-compute' + fake_service = dict(id=42, host='fake_host', binary='nova-compute', + topic='compute') + fake_compute = dict( + id=7116, service_id=42, host='fake_host', vcpus=0, memory_mb=0, + local_gb=0, vcpus_used=0, memory_mb_used=0, local_gb_used=0, + hypervisor_type=0, hypervisor_version=0, hypervisor_hostname=0, + free_ram_mb=0, free_disk_gb=0, current_workload=0, running_vms=0, + cpu_info='HAL', disk_available_least=0) + params_to_update = {'disabled': True, 'report_count': 13} + + ctxt = context.RequestContext('fake_user', 'fake_project', + is_admin=True) + # We use the real DB for this test, as it's too hard to reach the + # host_api to mock out its DB methods + db.service_create(ctxt, fake_service) + db.compute_node_create(ctxt, fake_compute) + + self.mox.ReplayAll() + + response = self.src_msg_runner.service_update( + ctxt, self.tgt_cell_name, + 'fake_host', binary, params_to_update) + result = response.value_or_raise() + result.pop('created_at', None) + result.pop('updated_at', None) + expected_result = dict( + deleted=0, deleted_at=None, + binary=fake_service['binary'], + disabled=True, # We just updated this.. + report_count=13, # ..and this + host='fake_host', id=42, + topic='compute') + self.assertEqual(expected_result, result) + def test_proxy_rpc_to_manager_call(self): fake_topic = 'fake-topic' fake_rpc_message = 'fake-rpc-message' diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index 0a5c6a71b..76c9f05d3 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -248,6 +248,20 @@ class CellsAPITestCase(test.TestCase): version='1.2') self.assertEqual(result, 'fake_response') + def test_service_update(self): + call_info = self._stub_rpc_method('call', 'fake_response') + result = self.cells_rpcapi.service_update( + self.fake_context, host_name='fake-host-name', + binary='nova-api', params_to_update={'disabled': True}) + expected_args = { + 'host_name': 'fake-host-name', + 'binary': 'nova-api', + 'params_to_update': {'disabled': True}} + self._check_result(call_info, 'service_update', + expected_args, + version='1.7') + self.assertEqual(result, 'fake_response') + def test_proxy_rpc_to_manager(self): call_info = self._stub_rpc_method('call', 'fake_response') result = self.cells_rpcapi.proxy_rpc_to_manager( diff --git a/nova/tests/compute/test_host_api.py b/nova/tests/compute/test_host_api.py index 66dd1d739..6a87205ae 100644 --- a/nova/tests/compute/test_host_api.py +++ b/nova/tests/compute/test_host_api.py @@ -205,6 +205,27 @@ class ComputeHostAPITestCase(test.TestCase): 'fake-host') self.assertEqual('fake-response', result) + def test_service_update(self): + host_name = 'fake-host' + binary = 'nova-compute' + params_to_update = dict(disabled=True) + service_id = 42 + expected_result = {'id': service_id} + + self.mox.StubOutWithMock(self.host_api.db, 'service_get_by_args') + self.host_api.db.service_get_by_args(self.ctxt, + host_name, binary).AndReturn({'id': service_id}) + + self.mox.StubOutWithMock(self.host_api.db, 'service_update') + self.host_api.db.service_update( + self.ctxt, service_id, params_to_update).AndReturn(expected_result) + + self.mox.ReplayAll() + + result = self.host_api.service_update( + self.ctxt, host_name, binary, params_to_update) + self.assertEqual(expected_result, result) + def test_instance_get_all_by_host(self): self.mox.StubOutWithMock(self.host_api.db, 'instance_get_all_by_host') @@ -312,6 +333,24 @@ class ComputeHostAPICellsTestCase(ComputeHostAPITestCase): 'fake-host') self.assertEqual('fake-response', result) + def test_service_update(self): + host_name = 'fake-host' + binary = 'nova-compute' + params_to_update = dict(disabled=True) + service_id = 42 + expected_result = {'id': service_id} + + self.mox.StubOutWithMock(self.host_api.cells_rpcapi, 'service_update') + self.host_api.cells_rpcapi.service_update( + self.ctxt, host_name, + binary, params_to_update).AndReturn(expected_result) + + self.mox.ReplayAll() + + result = self.host_api.service_update( + self.ctxt, host_name, binary, params_to_update) + self.assertEqual(expected_result, result) + def test_instance_get_all_by_host(self): instances = [dict(id=1, cell_name='cell1', host='host1'), dict(id=2, cell_name='cell2', host='host1'), |
