From ff80032bd40b2d4f8f9f663a2c56617b5ba65ef5 Mon Sep 17 00:00:00 2001 From: Roman Dobosz Date: Tue, 7 Jul 2015 14:05:52 +0200 Subject: [PATCH] New nova API call to mark nova-compute down Introducing new API call for changing the new flag state for forcing nova-compute state. This is done via adding new forced_down field to the Service objects and its check in timeout affected service groups drivers. Blueprint mark-host-down APIImpact Change-Id: I39f1a84c100726f87a4dc464dd9922d66efdb53f --- .../versions/versions-get-resp.json | 2 +- .../v2.11/service-disable-log-put-req.json | 5 + .../v2.11/service-disable-log-put-resp.json | 8 + .../v2.11/service-disable-put-req.json | 4 + .../v2.11/service-disable-put-resp.json | 7 + .../v2.11/service-enable-put-req.json | 4 + .../v2.11/service-enable-put-resp.json | 7 + .../v2.11/service-force-down-put-req.json | 5 + .../v2.11/service-force-down-put-resp.json | 7 + .../v2.11/services-list-get-resp.json | 48 +++ nova/api/openstack/api_version_request.py | 3 +- .../openstack/compute/plugins/v3/services.py | 75 ++++- .../openstack/compute/schemas/v3/services.py | 20 +- .../openstack/rest_api_version_history.rst | 6 + .../versions/versions-get-resp.json.tpl | 2 +- .../service-disable-log-put-req.json.tpl | 5 + .../service-disable-log-put-resp.json.tpl | 8 + .../v2.11/service-disable-put-req.json.tpl | 4 + .../v2.11/service-disable-put-resp.json.tpl | 7 + .../v2.11/service-enable-put-req.json.tpl | 4 + .../v2.11/service-enable-put-resp.json.tpl | 7 + .../v2.11/service-force-down-put-req.json.tpl | 5 + .../service-force-down-put-resp.json.tpl | 7 + .../v2.11/services-list-get-resp.json.tpl | 48 +++ nova/tests/functional/v3/test_services.py | 53 ++- .../compute/contrib/test_services.py | 309 +++++++++++++++++- .../api/openstack/compute/test_versions.py | 4 +- 27 files changed, 622 insertions(+), 42 deletions(-) create mode 100644 doc/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-disable-put-req.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-disable-put-resp.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-enable-put-req.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-enable-put-resp.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-force-down-put-req.json create mode 100644 doc/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json create mode 100644 doc/v3/api_samples/os-services/v2.11/services-list-get-resp.json create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-req.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-resp.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-req.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-resp.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-req.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json.tpl create mode 100644 nova/tests/functional/v3/api_samples/os-services/v2.11/services-list-get-resp.json.tpl diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json index f32b72c513..13123a6117 100644 --- a/doc/api_samples/versions/versions-get-resp.json +++ b/doc/api_samples/versions/versions-get-resp.json @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.10", + "version": "2.11", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json b/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json new file mode 100644 index 0000000000..b67b61751e --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json @@ -0,0 +1,5 @@ +{ + "host": "host1", + "binary": "nova-compute", + "disabled_reason": "test2" +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json b/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json new file mode 100644 index 0000000000..442e2099f9 --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json @@ -0,0 +1,8 @@ +{ + "service": { + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "status": "disabled" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-disable-put-req.json b/doc/v3/api_samples/os-services/v2.11/service-disable-put-req.json new file mode 100644 index 0000000000..a5f5f4f00d --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-disable-put-req.json @@ -0,0 +1,4 @@ +{ + "host": "host1", + "binary": "nova-compute" +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-disable-put-resp.json b/doc/v3/api_samples/os-services/v2.11/service-disable-put-resp.json new file mode 100644 index 0000000000..d7fe948d01 --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-disable-put-resp.json @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "status": "disabled" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-enable-put-req.json b/doc/v3/api_samples/os-services/v2.11/service-enable-put-req.json new file mode 100644 index 0000000000..a5f5f4f00d --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-enable-put-req.json @@ -0,0 +1,4 @@ +{ + "host": "host1", + "binary": "nova-compute" +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-enable-put-resp.json b/doc/v3/api_samples/os-services/v2.11/service-enable-put-resp.json new file mode 100644 index 0000000000..0a6b2668df --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-enable-put-resp.json @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "status": "enabled" + } +} \ No newline at end of file diff --git a/doc/v3/api_samples/os-services/v2.11/service-force-down-put-req.json b/doc/v3/api_samples/os-services/v2.11/service-force-down-put-req.json new file mode 100644 index 0000000000..904ae6504e --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-force-down-put-req.json @@ -0,0 +1,5 @@ +{ + "host": "host1", + "binary": "nova-compute", + "forced_down": true +} diff --git a/doc/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json b/doc/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json new file mode 100644 index 0000000000..4cdba98d2d --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "forced_down": true + } +} diff --git a/doc/v3/api_samples/os-services/v2.11/services-list-get-resp.json b/doc/v3/api_samples/os-services/v2.11/services-list-get-resp.json new file mode 100644 index 0000000000..ebef3e1b4e --- /dev/null +++ b/doc/v3/api_samples/os-services/v2.11/services-list-get-resp.json @@ -0,0 +1,48 @@ +{ + "services": [ + { + "id": 1, + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:02.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": 2, + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "state": "up", + "status": "disabled", + "updated_at": "2012-10-29T13:42:05.000000", + "forced_down": false, + "zone": "nova" + }, + { + "id": 3, + "binary": "nova-scheduler", + "disabled_reason": null, + "host": "host2", + "state": "down", + "status": "enabled", + "updated_at": "2012-09-19T06:55:34.000000", + "forced_down": false, + "zone": "internal" + }, + { + "id": 4, + "binary": "nova-compute", + "disabled_reason": "test4", + "host": "host2", + "state": "down", + "status": "disabled", + "updated_at": "2012-09-18T08:03:38.000000", + "forced_down": false, + "zone": "nova" + } + ] +} diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py index c8d60c1a2d..3475dceb74 100644 --- a/nova/api/openstack/api_version_request.py +++ b/nova/api/openstack/api_version_request.py @@ -50,6 +50,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 2.9 - Exposes lock information in server details. * 2.10 - Allow admins to query, create and delete keypairs owned by any user. + * 2.11 - Exposes forced_down attribute for os-services """ # The minimum and maximum versions of the API supported @@ -58,7 +59,7 @@ REST_API_VERSION_HISTORY = """REST API Version History: # Note(cyeoh): This only applies for the v2.1 API once microversions # support is fully merged. It does not affect the V2 API. _MIN_API_VERSION = "2.1" -_MAX_API_VERSION = "2.10" +_MAX_API_VERSION = "2.11" DEFAULT_API_VERSION = _MIN_API_VERSION diff --git a/nova/api/openstack/compute/plugins/v3/services.py b/nova/api/openstack/compute/plugins/v3/services.py index d1862b91b5..a522694feb 100644 --- a/nova/api/openstack/compute/plugins/v3/services.py +++ b/nova/api/openstack/compute/plugins/v3/services.py @@ -14,6 +14,7 @@ import webob.exc +from nova.api.openstack import api_version_request from nova.api.openstack.compute.schemas.v3 import services from nova.api.openstack import extensions from nova.api.openstack import wsgi @@ -32,6 +33,9 @@ class ServiceController(wsgi.Controller): def __init__(self): self.host_api = compute.HostAPI() self.servicegroup_api = servicegroup.API() + self.actions = {"enable": self._enable, + "disable": self._disable, + "disable-log-reason": self._disable_log_reason} def _get_services(self, req): context = req.environ['nova.context'] @@ -51,7 +55,7 @@ class ServiceController(wsgi.Controller): return _services - def _get_service_detail(self, svc): + def _get_service_detail(self, svc, additional_fields): alive = self.servicegroup_api.service_is_up(svc) state = (alive and "up") or "down" active = 'enabled' @@ -66,11 +70,15 @@ class ServiceController(wsgi.Controller): 'updated_at': svc['updated_at'], 'disabled_reason': svc['disabled_reason']} + for field in additional_fields: + service_detail[field] = svc[field] + return service_detail - def _get_services_list(self, req): + def _get_services_list(self, req, additional_fields=()): _services = self._get_services(req) - return [self._get_service_detail(svc) for svc in _services] + return [self._get_service_detail(svc, additional_fields) + for svc in _services] def _enable(self, body, context): """Enable scheduling for a service.""" @@ -112,6 +120,23 @@ class ServiceController(wsgi.Controller): self._update(context, body['host'], body['binary'], params_to_update) return ret_value + def _forced_down(self, body, context): + """Set or unset forced_down flag for the service""" + try: + forced_down = body["forced_down"] + except KeyError: + msg = _('Missing forced_down field') + raise webob.exc.HTTPBadRequest(explanation=msg) + + host = body['host'] + binary = body['binary'] + + ret_value = {'service': {'host': host, + 'binary': binary, + 'forced_down': forced_down}} + self._update(context, host, binary, {"forced_down": forced_down}) + return ret_value + def _update(self, context, host, binary, payload): """Do the actual PUT/update""" try: @@ -119,6 +144,19 @@ class ServiceController(wsgi.Controller): except exception.HostBinaryNotFound as exc: raise webob.exc.HTTPNotFound(explanation=exc.format_message()) + def _perform_action(self, req, id, body, actions): + """Calculate action dictionary dependent on provided fields""" + context = req.environ['nova.context'] + authorize(context) + + try: + action = actions[id] + except KeyError: + msg = _("Unknown action") + raise webob.exc.HTTPNotFound(explanation=msg) + + return action(body, context) + @wsgi.response(204) @extensions.expected_errors(404) def delete(self, req, id): @@ -137,26 +175,29 @@ class ServiceController(wsgi.Controller): """Return a list of all running services. Filter by host & service name """ - return {'services': self._get_services_list(req)} + req_ver = req.api_version_request + if req_ver >= api_version_request.APIVersionRequest("2.11"): + _services = self._get_services_list(req, ['forced_down']) + else: + _services = self._get_services_list(req) + return {'services': _services} + + @wsgi.Controller.api_version('2.1', '2.10') @extensions.expected_errors((400, 404)) @validation.schema(services.service_update) def update(self, req, id, body): """Perform service update""" - context = req.environ['nova.context'] - authorize(context) + return self._perform_action(req, id, body, self.actions) - actions = {"enable": self._enable, - "disable": self._disable, - "disable-log-reason": self._disable_log_reason} - - try: - action = actions[id] - except KeyError: - msg = _("Unknown action") - raise webob.exc.HTTPNotFound(explanation=msg) - - return action(body, context) + @wsgi.Controller.api_version('2.11') # noqa + @extensions.expected_errors((400, 404)) + @validation.schema(services.service_update_v211) + def update(self, req, id, body): + """Perform service update""" + actions = self.actions.copy() + actions["force-down"] = self._forced_down + return self._perform_action(req, id, body, actions) class Services(extensions.V3APIExtensionBase): diff --git a/nova/api/openstack/compute/schemas/v3/services.py b/nova/api/openstack/compute/schemas/v3/services.py index 93b2b192c4..f62dd0ccf5 100644 --- a/nova/api/openstack/compute/schemas/v3/services.py +++ b/nova/api/openstack/compute/schemas/v3/services.py @@ -20,10 +20,26 @@ service_update = { 'host': parameter_types.hostname, 'binary': { 'type': 'string', 'minLength': 1, 'maxLength': 255, - }, + }, 'disabled_reason': { 'type': 'string', 'minLength': 1, 'maxLength': 255, - } + } + }, + 'required': ['host', 'binary'], + 'additionalProperties': False +} + +service_update_v211 = { + 'type': 'object', + 'properties': { + 'host': parameter_types.hostname, + 'binary': { + 'type': 'string', 'minLength': 1, 'maxLength': 255, + }, + 'disabled_reason': { + 'type': 'string', 'minLength': 1, 'maxLength': 255, + }, + 'forced_down': parameter_types.boolean }, 'required': ['host', 'binary'], 'additionalProperties': False diff --git a/nova/api/openstack/rest_api_version_history.rst b/nova/api/openstack/rest_api_version_history.rst index 8ea86f340f..0d3dbf9c9d 100644 --- a/nova/api/openstack/rest_api_version_history.rst +++ b/nova/api/openstack/rest_api_version_history.rst @@ -117,3 +117,9 @@ user documentation. Administrators will be able to list, get details and delete keypairs owned by users other than themselves and to create new keypairs on behalf of their users. + +2.11 +---- + + Exposed attribute ``forced_down`` for ``os-services``. + Added ability to change the ``forced_down`` attribute by calling an update. diff --git a/nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl b/nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl index f32b72c513..13123a6117 100644 --- a/nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl +++ b/nova/tests/functional/api_samples/versions/versions-get-resp.json.tpl @@ -22,7 +22,7 @@ } ], "status": "CURRENT", - "version": "2.10", + "version": "2.11", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z" } diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json.tpl new file mode 100644 index 0000000000..13ba2f11ca --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-req.json.tpl @@ -0,0 +1,5 @@ +{ + "host": "%(host)s", + "binary": "%(binary)s", + "disabled_reason": "%(disabled_reason)s" +} diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json.tpl new file mode 100644 index 0000000000..442e2099f9 --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-log-put-resp.json.tpl @@ -0,0 +1,8 @@ +{ + "service": { + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "status": "disabled" + } +} \ No newline at end of file diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-req.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-req.json.tpl new file mode 100644 index 0000000000..57182e935c --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-req.json.tpl @@ -0,0 +1,4 @@ +{ + "host": "%(host)s", + "binary": "%(binary)s" +} diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-resp.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-resp.json.tpl new file mode 100644 index 0000000000..d7fe948d01 --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-disable-put-resp.json.tpl @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "status": "disabled" + } +} \ No newline at end of file diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-req.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-req.json.tpl new file mode 100644 index 0000000000..57182e935c --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-req.json.tpl @@ -0,0 +1,4 @@ +{ + "host": "%(host)s", + "binary": "%(binary)s" +} diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-resp.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-resp.json.tpl new file mode 100644 index 0000000000..0a6b2668df --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-enable-put-resp.json.tpl @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "status": "enabled" + } +} \ No newline at end of file diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-req.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-req.json.tpl new file mode 100644 index 0000000000..fd49cfd447 --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-req.json.tpl @@ -0,0 +1,5 @@ +{ + "host": "%(host)s", + "binary": "%(binary)s", + "forced_down": %(forced_down)s +} diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json.tpl new file mode 100644 index 0000000000..4cdba98d2d --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/service-force-down-put-resp.json.tpl @@ -0,0 +1,7 @@ +{ + "service": { + "binary": "nova-compute", + "host": "host1", + "forced_down": true + } +} diff --git a/nova/tests/functional/v3/api_samples/os-services/v2.11/services-list-get-resp.json.tpl b/nova/tests/functional/v3/api_samples/os-services/v2.11/services-list-get-resp.json.tpl new file mode 100644 index 0000000000..87b2ba90bc --- /dev/null +++ b/nova/tests/functional/v3/api_samples/os-services/v2.11/services-list-get-resp.json.tpl @@ -0,0 +1,48 @@ +{ + "services": [ + { + "binary": "nova-scheduler", + "disabled_reason": "test1", + "host": "host1", + "id": 1, + "state": "up", + "status": "disabled", + "updated_at": "%(strtime)s", + "forced_down": false, + "zone": "internal" + }, + { + "binary": "nova-compute", + "disabled_reason": "test2", + "host": "host1", + "id": 2, + "state": "up", + "status": "disabled", + "updated_at": "%(strtime)s", + "forced_down": false, + "zone": "nova" + }, + { + "binary": "nova-scheduler", + "disabled_reason": null, + "host": "host2", + "id": 3, + "state": "down", + "status": "enabled", + "updated_at": "%(strtime)s", + "forced_down": false, + "zone": "internal" + }, + { + "binary": "nova-compute", + "disabled_reason": "test4", + "host": "host2", + "id": 4, + "state": "down", + "status": "disabled", + "updated_at": "%(strtime)s", + "forced_down": false, + "zone": "nova" + } + ] +} diff --git a/nova/tests/functional/v3/test_services.py b/nova/tests/functional/v3/test_services.py index f89625344b..5c983e9027 100644 --- a/nova/tests/functional/v3/test_services.py +++ b/nova/tests/functional/v3/test_services.py @@ -28,6 +28,8 @@ CONF.import_opt('osapi_compute_extension', class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): ADMIN_API = True extension_name = "os-services" + request_api_version = None + # TODO(gmann): Overriding '_api_version' till all functional tests # are merged between v2 and v2.1. After that base class variable # itself can be changed to 'v2' @@ -63,7 +65,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): def test_services_list(self): """Return a list of all agent builds.""" - response = self._do_get('os-services') + response = self._do_get('os-services', + api_version=self.request_api_version) subs = {'binary': 'nova-compute', 'host': 'host1', 'zone': 'nova', @@ -77,7 +80,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): subs = {"host": "host1", 'binary': 'nova-compute'} response = self._do_put('os-services/enable', - 'service-enable-put-req', subs) + 'service-enable-put-req', subs, + api_version=self.request_api_version) self._verify_response('service-enable-put-resp', subs, response, 200) def test_service_disable(self): @@ -85,7 +89,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): subs = {"host": "host1", 'binary': 'nova-compute'} response = self._do_put('os-services/disable', - 'service-disable-put-req', subs) + 'service-disable-put-req', subs, + api_version=self.request_api_version) self._verify_response('service-disable-put-resp', subs, response, 200) def test_service_disable_log_reason(self): @@ -94,12 +99,46 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): 'binary': 'nova-compute', 'disabled_reason': 'test2'} response = self._do_put('os-services/disable-log-reason', - 'service-disable-log-put-req', subs) - return self._verify_response('service-disable-log-put-resp', - subs, response, 200) + 'service-disable-log-put-req', subs, + api_version=self.request_api_version) + self._verify_response('service-disable-log-put-resp', + subs, response, 200) def test_service_delete(self): """Delete an existing service.""" - response = self._do_delete('os-services/1') + response = self._do_delete('os-services/1', + api_version=self.request_api_version) self.assertEqual(response.status_code, 204) self.assertEqual(response.content, "") + + +class ServicesV211JsonTest(ServicesJsonTest): + request_api_version = '2.11' + _api_version = 'v2' + # NOTE(gryf): There is no need to run those tests on v2 API. Only + # scenarios for v2_9 will be run. + scenarios = [('v2_11', {})] + + def test_services_list(self): + """Return a list of all agent builds.""" + response = self._do_get('os-services', + api_version=self.request_api_version) + subs = {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'forced_down': 'false', + 'status': 'disabled', + 'state': 'up'} + subs.update(self._get_regexes()) + self._verify_response('services-list-get-resp', subs, response, 200) + + def test_force_down(self): + """Set forced_down flag""" + subs = {"host": 'host1', + 'binary': 'nova-compute', + 'forced_down': 'true'} + response = self._do_put('os-services/force-down', + 'service-force-down-put-req', subs, + api_version=self.request_api_version) + self._verify_response('service-force-down-put-resp', subs, + response, 200) diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_services.py b/nova/tests/unit/api/openstack/compute/contrib/test_services.py index 2db3869972..336fb451fc 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_services.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_services.py @@ -21,9 +21,11 @@ import mock from oslo_utils import timeutils import webob.exc +from nova.api.openstack import api_version_request as api_version from nova.api.openstack.compute.contrib import services as services_v2 from nova.api.openstack.compute.plugins.v3 import services as services_v21 from nova.api.openstack import extensions +from nova.api.openstack import wsgi as os_wsgi from nova import availability_zones from nova.cells import utils as cells_utils from nova.compute import cells_api @@ -46,6 +48,7 @@ fake_services_list = [ topic='scheduler', updated_at=datetime.datetime(2012, 10, 29, 13, 42, 2), created_at=datetime.datetime(2012, 9, 18, 2, 46, 27), + forced_down=False, disabled_reason='test1'), dict(test_service.fake_service, binary='nova-compute', @@ -55,6 +58,7 @@ fake_services_list = [ topic='compute', updated_at=datetime.datetime(2012, 10, 29, 13, 42, 5), created_at=datetime.datetime(2012, 9, 18, 2, 46, 27), + forced_down=False, disabled_reason='test2'), dict(test_service.fake_service, binary='nova-scheduler', @@ -64,6 +68,7 @@ fake_services_list = [ topic='scheduler', updated_at=datetime.datetime(2012, 9, 19, 6, 55, 34), created_at=datetime.datetime(2012, 9, 18, 2, 46, 28), + forced_down=False, disabled_reason=None), dict(test_service.fake_service, binary='nova-compute', @@ -73,28 +78,30 @@ fake_services_list = [ topic='compute', updated_at=datetime.datetime(2012, 9, 18, 8, 3, 38), created_at=datetime.datetime(2012, 9, 18, 2, 46, 28), + forced_down=False, disabled_reason='test4'), ] class FakeRequest(object): - environ = {"nova.context": context.get_admin_context()} - GET = {} + environ = {"nova.context": context.get_admin_context()} + GET = {} + + def __init__(self, version=os_wsgi.DEFAULT_API_VERSION): # version='2.1'): + super(FakeRequest, self).__init__() + self.api_version_request = api_version.APIVersionRequest(version) -class FakeRequestWithService(object): - environ = {"nova.context": context.get_admin_context()} - GET = {"binary": "nova-compute"} +class FakeRequestWithService(FakeRequest): + GET = {"binary": "nova-compute"} -class FakeRequestWithHost(object): - environ = {"nova.context": context.get_admin_context()} - GET = {"host": "host1"} +class FakeRequestWithHost(FakeRequest): + GET = {"host": "host1"} -class FakeRequestWithHostService(object): - environ = {"nova.context": context.get_admin_context()} - GET = {"host": "host1", "binary": "nova-compute"} +class FakeRequestWithHostService(FakeRequest): + GET = {"host": "host1", "binary": "nova-compute"} def fake_service_get_all(services): @@ -160,6 +167,7 @@ def fake_utcnow_ts(): class ServicesTestV21(test.TestCase): service_is_up_exc = webob.exc.HTTPInternalServerError bad_request = exception.ValidationError + wsgi_api_version = os_wsgi.DEFAULT_API_VERSION def _set_up_controller(self): self.controller = services_v21.ServiceController() @@ -551,6 +559,285 @@ class ServicesTestV21(test.TestCase): self.assertRaises(self.service_is_up_exc, self.controller.index, req) +class ServicesTestV211(ServicesTestV21): + wsgi_api_version = '2.11' + + def test_services_list(self): + req = FakeRequest(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-scheduler', + 'host': 'host1', + 'zone': 'internal', + 'status': 'disabled', + 'id': 1, + 'state': 'up', + 'forced_down': False, + 'disabled_reason': 'test1', + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2)}, + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'id': 2, + 'status': 'disabled', + 'disabled_reason': 'test2', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}, + {'binary': 'nova-scheduler', + 'host': 'host2', + 'zone': 'internal', + 'id': 3, + 'status': 'enabled', + 'disabled_reason': None, + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34)}, + {'binary': 'nova-compute', + 'host': 'host2', + 'zone': 'nova', + 'id': 4, + 'status': 'disabled', + 'disabled_reason': 'test4', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38)}]} + self._process_output(response) + self.assertEqual(res_dict, response) + + def test_services_list_with_host(self): + req = FakeRequestWithHost(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-scheduler', + 'host': 'host1', + 'disabled_reason': 'test1', + 'id': 1, + 'zone': 'internal', + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2)}, + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'disabled_reason': 'test2', + 'id': 2, + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}]} + self._process_output(response) + self.assertEqual(res_dict, response) + + def test_services_list_with_service(self): + req = FakeRequestWithService(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-compute', + 'host': 'host1', + 'disabled_reason': 'test2', + 'id': 2, + 'zone': 'nova', + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}, + {'binary': 'nova-compute', + 'host': 'host2', + 'zone': 'nova', + 'disabled_reason': 'test4', + 'id': 4, + 'status': 'disabled', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38)}]} + self._process_output(response) + self.assertEqual(res_dict, response) + + def test_services_list_with_host_service(self): + req = FakeRequestWithHostService(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'disabled_reason': 'test2', + 'id': 2, + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}]} + self._process_output(response) + self.assertEqual(res_dict, response) + + def test_services_detail(self): + self.ext_mgr.extensions['os-extended-services'] = True + req = FakeRequest(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-scheduler', + 'host': 'host1', + 'zone': 'internal', + 'status': 'disabled', + 'id': 1, + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), + 'disabled_reason': 'test1'}, + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'status': 'disabled', + 'state': 'up', + 'id': 2, + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5), + 'disabled_reason': 'test2'}, + {'binary': 'nova-scheduler', + 'host': 'host2', + 'zone': 'internal', + 'status': 'enabled', + 'id': 3, + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34), + 'disabled_reason': None}, + {'binary': 'nova-compute', + 'host': 'host2', + 'zone': 'nova', + 'id': 4, + 'status': 'disabled', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38), + 'disabled_reason': 'test4'}]} + self._process_output(response, has_disabled=True) + self.assertEqual(res_dict, response) + + def test_service_detail_with_host(self): + self.ext_mgr.extensions['os-extended-services'] = True + req = FakeRequestWithHost(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-scheduler', + 'host': 'host1', + 'zone': 'internal', + 'id': 1, + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2), + 'disabled_reason': 'test1'}, + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'id': 2, + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5), + 'disabled_reason': 'test2'}]} + self._process_output(response, has_disabled=True) + self.assertEqual(res_dict, response) + + def test_service_detail_with_service(self): + self.ext_mgr.extensions['os-extended-services'] = True + req = FakeRequestWithService(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'id': 2, + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5), + 'disabled_reason': 'test2'}, + {'binary': 'nova-compute', + 'host': 'host2', + 'id': 4, + 'zone': 'nova', + 'status': 'disabled', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38), + 'disabled_reason': 'test4'}]} + self._process_output(response, has_disabled=True) + self.assertEqual(res_dict, response) + + def test_service_detail_with_host_service(self): + self.ext_mgr.extensions['os-extended-services'] = True + req = FakeRequestWithHostService(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-compute', + 'host': 'host1', + 'zone': 'nova', + 'status': 'disabled', + 'id': 2, + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5), + 'disabled_reason': 'test2'}]} + self._process_output(response, has_disabled=True) + self.assertEqual(res_dict, response) + + def test_services_detail_with_delete_extension(self): + self.ext_mgr.extensions['os-extended-services-delete'] = True + req = FakeRequest(self.wsgi_api_version) + res_dict = self.controller.index(req) + + response = {'services': [ + {'binary': 'nova-scheduler', + 'host': 'host1', + 'id': 1, + 'zone': 'internal', + 'disabled_reason': 'test1', + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2)}, + {'binary': 'nova-compute', + 'host': 'host1', + 'id': 2, + 'zone': 'nova', + 'disabled_reason': 'test2', + 'status': 'disabled', + 'state': 'up', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}, + {'binary': 'nova-scheduler', + 'host': 'host2', + 'disabled_reason': None, + 'id': 3, + 'zone': 'internal', + 'status': 'enabled', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34)}, + {'binary': 'nova-compute', + 'host': 'host2', + 'id': 4, + 'disabled_reason': 'test4', + 'zone': 'nova', + 'status': 'disabled', + 'state': 'down', + 'forced_down': False, + 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38)}]} + self._process_output(response, has_id=True) + self.assertEqual(res_dict, response) + + class ServicesTestV20(ServicesTestV21): service_is_up_exc = KeyError bad_request = webob.exc.HTTPBadRequest diff --git a/nova/tests/unit/api/openstack/compute/test_versions.py b/nova/tests/unit/api/openstack/compute/test_versions.py index 86229c043a..3425dd82e7 100644 --- a/nova/tests/unit/api/openstack/compute/test_versions.py +++ b/nova/tests/unit/api/openstack/compute/test_versions.py @@ -65,7 +65,7 @@ EXP_VERSIONS = { "v2.1": { "id": "v2.1", "status": "CURRENT", - "version": "2.10", + "version": "2.11", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [ @@ -114,7 +114,7 @@ class VersionsTestV20(test.NoDBTestCase): { "id": "v2.1", "status": "CURRENT", - "version": "2.10", + "version": "2.11", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [