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
This commit is contained in:
Roman Dobosz 2015-07-07 14:05:52 +02:00
parent 89660d6ccd
commit ff80032bd4
27 changed files with 622 additions and 42 deletions

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.10", "version": "2.11",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -0,0 +1,5 @@
{
"host": "host1",
"binary": "nova-compute",
"disabled_reason": "test2"
}

View File

@ -0,0 +1,8 @@
{
"service": {
"binary": "nova-compute",
"disabled_reason": "test2",
"host": "host1",
"status": "disabled"
}
}

View File

@ -0,0 +1,4 @@
{
"host": "host1",
"binary": "nova-compute"
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"status": "disabled"
}
}

View File

@ -0,0 +1,4 @@
{
"host": "host1",
"binary": "nova-compute"
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"status": "enabled"
}
}

View File

@ -0,0 +1,5 @@
{
"host": "host1",
"binary": "nova-compute",
"forced_down": true
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"forced_down": true
}
}

View File

@ -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"
}
]
}

View File

@ -50,6 +50,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
* 2.9 - Exposes lock information in server details. * 2.9 - Exposes lock information in server details.
* 2.10 - Allow admins to query, create and delete keypairs owned by any * 2.10 - Allow admins to query, create and delete keypairs owned by any
user. user.
* 2.11 - Exposes forced_down attribute for os-services
""" """
# The minimum and maximum versions of the API supported # 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 # Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API. # support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1" _MIN_API_VERSION = "2.1"
_MAX_API_VERSION = "2.10" _MAX_API_VERSION = "2.11"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -14,6 +14,7 @@
import webob.exc import webob.exc
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas.v3 import services from nova.api.openstack.compute.schemas.v3 import services
from nova.api.openstack import extensions from nova.api.openstack import extensions
from nova.api.openstack import wsgi from nova.api.openstack import wsgi
@ -32,6 +33,9 @@ class ServiceController(wsgi.Controller):
def __init__(self): def __init__(self):
self.host_api = compute.HostAPI() self.host_api = compute.HostAPI()
self.servicegroup_api = servicegroup.API() 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): def _get_services(self, req):
context = req.environ['nova.context'] context = req.environ['nova.context']
@ -51,7 +55,7 @@ class ServiceController(wsgi.Controller):
return _services return _services
def _get_service_detail(self, svc): def _get_service_detail(self, svc, additional_fields):
alive = self.servicegroup_api.service_is_up(svc) alive = self.servicegroup_api.service_is_up(svc)
state = (alive and "up") or "down" state = (alive and "up") or "down"
active = 'enabled' active = 'enabled'
@ -66,11 +70,15 @@ class ServiceController(wsgi.Controller):
'updated_at': svc['updated_at'], 'updated_at': svc['updated_at'],
'disabled_reason': svc['disabled_reason']} 'disabled_reason': svc['disabled_reason']}
for field in additional_fields:
service_detail[field] = svc[field]
return service_detail return service_detail
def _get_services_list(self, req): def _get_services_list(self, req, additional_fields=()):
_services = self._get_services(req) _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): def _enable(self, body, context):
"""Enable scheduling for a service.""" """Enable scheduling for a service."""
@ -112,6 +120,23 @@ class ServiceController(wsgi.Controller):
self._update(context, body['host'], body['binary'], params_to_update) self._update(context, body['host'], body['binary'], params_to_update)
return ret_value 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): def _update(self, context, host, binary, payload):
"""Do the actual PUT/update""" """Do the actual PUT/update"""
try: try:
@ -119,6 +144,19 @@ class ServiceController(wsgi.Controller):
except exception.HostBinaryNotFound as exc: except exception.HostBinaryNotFound as exc:
raise webob.exc.HTTPNotFound(explanation=exc.format_message()) 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) @wsgi.response(204)
@extensions.expected_errors(404) @extensions.expected_errors(404)
def delete(self, req, id): def delete(self, req, id):
@ -137,26 +175,29 @@ class ServiceController(wsgi.Controller):
"""Return a list of all running services. Filter by host & service """Return a list of all running services. Filter by host & service
name 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)) @extensions.expected_errors((400, 404))
@validation.schema(services.service_update) @validation.schema(services.service_update)
def update(self, req, id, body): def update(self, req, id, body):
"""Perform service update""" """Perform service update"""
context = req.environ['nova.context'] return self._perform_action(req, id, body, self.actions)
authorize(context)
actions = {"enable": self._enable, @wsgi.Controller.api_version('2.11') # noqa
"disable": self._disable, @extensions.expected_errors((400, 404))
"disable-log-reason": self._disable_log_reason} @validation.schema(services.service_update_v211)
def update(self, req, id, body):
try: """Perform service update"""
action = actions[id] actions = self.actions.copy()
except KeyError: actions["force-down"] = self._forced_down
msg = _("Unknown action") return self._perform_action(req, id, body, actions)
raise webob.exc.HTTPNotFound(explanation=msg)
return action(body, context)
class Services(extensions.V3APIExtensionBase): class Services(extensions.V3APIExtensionBase):

View File

@ -20,10 +20,26 @@ service_update = {
'host': parameter_types.hostname, 'host': parameter_types.hostname,
'binary': { 'binary': {
'type': 'string', 'minLength': 1, 'maxLength': 255, 'type': 'string', 'minLength': 1, 'maxLength': 255,
}, },
'disabled_reason': { 'disabled_reason': {
'type': 'string', 'minLength': 1, 'maxLength': 255, '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'], 'required': ['host', 'binary'],
'additionalProperties': False 'additionalProperties': False

View File

@ -117,3 +117,9 @@ user documentation.
Administrators will be able to list, get details and delete keypairs owned by 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 other than themselves and to create new keypairs on behalf of their
users. users.
2.11
----
Exposed attribute ``forced_down`` for ``os-services``.
Added ability to change the ``forced_down`` attribute by calling an update.

View File

@ -22,7 +22,7 @@
} }
], ],
"status": "CURRENT", "status": "CURRENT",
"version": "2.10", "version": "2.11",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z" "updated": "2013-07-23T11:33:21Z"
} }

View File

@ -0,0 +1,5 @@
{
"host": "%(host)s",
"binary": "%(binary)s",
"disabled_reason": "%(disabled_reason)s"
}

View File

@ -0,0 +1,8 @@
{
"service": {
"binary": "nova-compute",
"disabled_reason": "test2",
"host": "host1",
"status": "disabled"
}
}

View File

@ -0,0 +1,4 @@
{
"host": "%(host)s",
"binary": "%(binary)s"
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"status": "disabled"
}
}

View File

@ -0,0 +1,4 @@
{
"host": "%(host)s",
"binary": "%(binary)s"
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"status": "enabled"
}
}

View File

@ -0,0 +1,5 @@
{
"host": "%(host)s",
"binary": "%(binary)s",
"forced_down": %(forced_down)s
}

View File

@ -0,0 +1,7 @@
{
"service": {
"binary": "nova-compute",
"host": "host1",
"forced_down": true
}
}

View File

@ -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"
}
]
}

View File

@ -28,6 +28,8 @@ CONF.import_opt('osapi_compute_extension',
class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3): class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3):
ADMIN_API = True ADMIN_API = True
extension_name = "os-services" extension_name = "os-services"
request_api_version = None
# TODO(gmann): Overriding '_api_version' till all functional tests # TODO(gmann): Overriding '_api_version' till all functional tests
# are merged between v2 and v2.1. After that base class variable # are merged between v2 and v2.1. After that base class variable
# itself can be changed to 'v2' # itself can be changed to 'v2'
@ -63,7 +65,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3):
def test_services_list(self): def test_services_list(self):
"""Return a list of all agent builds.""" """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', subs = {'binary': 'nova-compute',
'host': 'host1', 'host': 'host1',
'zone': 'nova', 'zone': 'nova',
@ -77,7 +80,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3):
subs = {"host": "host1", subs = {"host": "host1",
'binary': 'nova-compute'} 'binary': 'nova-compute'}
response = self._do_put('os-services/enable', 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) self._verify_response('service-enable-put-resp', subs, response, 200)
def test_service_disable(self): def test_service_disable(self):
@ -85,7 +89,8 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3):
subs = {"host": "host1", subs = {"host": "host1",
'binary': 'nova-compute'} 'binary': 'nova-compute'}
response = self._do_put('os-services/disable', 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) self._verify_response('service-disable-put-resp', subs, response, 200)
def test_service_disable_log_reason(self): def test_service_disable_log_reason(self):
@ -94,12 +99,46 @@ class ServicesJsonTest(api_sample_base.ApiSampleTestBaseV3):
'binary': 'nova-compute', 'binary': 'nova-compute',
'disabled_reason': 'test2'} 'disabled_reason': 'test2'}
response = self._do_put('os-services/disable-log-reason', response = self._do_put('os-services/disable-log-reason',
'service-disable-log-put-req', subs) 'service-disable-log-put-req', subs,
return self._verify_response('service-disable-log-put-resp', api_version=self.request_api_version)
subs, response, 200) self._verify_response('service-disable-log-put-resp',
subs, response, 200)
def test_service_delete(self): def test_service_delete(self):
"""Delete an existing service.""" """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.status_code, 204)
self.assertEqual(response.content, "") 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)

View File

@ -21,9 +21,11 @@ import mock
from oslo_utils import timeutils from oslo_utils import timeutils
import webob.exc 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.contrib import services as services_v2
from nova.api.openstack.compute.plugins.v3 import services as services_v21 from nova.api.openstack.compute.plugins.v3 import services as services_v21
from nova.api.openstack import extensions from nova.api.openstack import extensions
from nova.api.openstack import wsgi as os_wsgi
from nova import availability_zones from nova import availability_zones
from nova.cells import utils as cells_utils from nova.cells import utils as cells_utils
from nova.compute import cells_api from nova.compute import cells_api
@ -46,6 +48,7 @@ fake_services_list = [
topic='scheduler', topic='scheduler',
updated_at=datetime.datetime(2012, 10, 29, 13, 42, 2), updated_at=datetime.datetime(2012, 10, 29, 13, 42, 2),
created_at=datetime.datetime(2012, 9, 18, 2, 46, 27), created_at=datetime.datetime(2012, 9, 18, 2, 46, 27),
forced_down=False,
disabled_reason='test1'), disabled_reason='test1'),
dict(test_service.fake_service, dict(test_service.fake_service,
binary='nova-compute', binary='nova-compute',
@ -55,6 +58,7 @@ fake_services_list = [
topic='compute', topic='compute',
updated_at=datetime.datetime(2012, 10, 29, 13, 42, 5), updated_at=datetime.datetime(2012, 10, 29, 13, 42, 5),
created_at=datetime.datetime(2012, 9, 18, 2, 46, 27), created_at=datetime.datetime(2012, 9, 18, 2, 46, 27),
forced_down=False,
disabled_reason='test2'), disabled_reason='test2'),
dict(test_service.fake_service, dict(test_service.fake_service,
binary='nova-scheduler', binary='nova-scheduler',
@ -64,6 +68,7 @@ fake_services_list = [
topic='scheduler', topic='scheduler',
updated_at=datetime.datetime(2012, 9, 19, 6, 55, 34), updated_at=datetime.datetime(2012, 9, 19, 6, 55, 34),
created_at=datetime.datetime(2012, 9, 18, 2, 46, 28), created_at=datetime.datetime(2012, 9, 18, 2, 46, 28),
forced_down=False,
disabled_reason=None), disabled_reason=None),
dict(test_service.fake_service, dict(test_service.fake_service,
binary='nova-compute', binary='nova-compute',
@ -73,28 +78,30 @@ fake_services_list = [
topic='compute', topic='compute',
updated_at=datetime.datetime(2012, 9, 18, 8, 3, 38), updated_at=datetime.datetime(2012, 9, 18, 8, 3, 38),
created_at=datetime.datetime(2012, 9, 18, 2, 46, 28), created_at=datetime.datetime(2012, 9, 18, 2, 46, 28),
forced_down=False,
disabled_reason='test4'), disabled_reason='test4'),
] ]
class FakeRequest(object): class FakeRequest(object):
environ = {"nova.context": context.get_admin_context()} environ = {"nova.context": context.get_admin_context()}
GET = {} 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): class FakeRequestWithService(FakeRequest):
environ = {"nova.context": context.get_admin_context()} GET = {"binary": "nova-compute"}
GET = {"binary": "nova-compute"}
class FakeRequestWithHost(object): class FakeRequestWithHost(FakeRequest):
environ = {"nova.context": context.get_admin_context()} GET = {"host": "host1"}
GET = {"host": "host1"}
class FakeRequestWithHostService(object): class FakeRequestWithHostService(FakeRequest):
environ = {"nova.context": context.get_admin_context()} GET = {"host": "host1", "binary": "nova-compute"}
GET = {"host": "host1", "binary": "nova-compute"}
def fake_service_get_all(services): def fake_service_get_all(services):
@ -160,6 +167,7 @@ def fake_utcnow_ts():
class ServicesTestV21(test.TestCase): class ServicesTestV21(test.TestCase):
service_is_up_exc = webob.exc.HTTPInternalServerError service_is_up_exc = webob.exc.HTTPInternalServerError
bad_request = exception.ValidationError bad_request = exception.ValidationError
wsgi_api_version = os_wsgi.DEFAULT_API_VERSION
def _set_up_controller(self): def _set_up_controller(self):
self.controller = services_v21.ServiceController() self.controller = services_v21.ServiceController()
@ -551,6 +559,285 @@ class ServicesTestV21(test.TestCase):
self.assertRaises(self.service_is_up_exc, self.controller.index, req) 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): class ServicesTestV20(ServicesTestV21):
service_is_up_exc = KeyError service_is_up_exc = KeyError
bad_request = webob.exc.HTTPBadRequest bad_request = webob.exc.HTTPBadRequest

View File

@ -65,7 +65,7 @@ EXP_VERSIONS = {
"v2.1": { "v2.1": {
"id": "v2.1", "id": "v2.1",
"status": "CURRENT", "status": "CURRENT",
"version": "2.10", "version": "2.11",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z", "updated": "2013-07-23T11:33:21Z",
"links": [ "links": [
@ -114,7 +114,7 @@ class VersionsTestV20(test.NoDBTestCase):
{ {
"id": "v2.1", "id": "v2.1",
"status": "CURRENT", "status": "CURRENT",
"version": "2.10", "version": "2.11",
"min_version": "2.1", "min_version": "2.1",
"updated": "2013-07-23T11:33:21Z", "updated": "2013-07-23T11:33:21Z",
"links": [ "links": [