elem.set('status')
elem.set('state')
elem.set('update_at')
+ elem.set('disabled_reason')
return xmlutil.MasterTemplate(root, 1)
root.set('disabled')
root.set('binary')
root.set('status')
+ root.set('disabled_reason')
return xmlutil.MasterTemplate(root, 1)
class ServiceController(wsgi.Controller):
+ def __init__(self, ext_mgr=None):
+ self.ext_mgr = ext_mgr
+ super(ServiceController, self).__init__()
+
@wsgi.serializers(xml=ServicesIndexTemplate)
def index(self, req):
"""Return a list of all running services.
"""
context = req.environ['cinder.context']
authorize(context)
+ detailed = self.ext_mgr.is_loaded('os-extended-services')
now = timeutils.utcnow()
services = db.service_get_all(context)
active = 'enabled'
if svc['disabled']:
active = 'disabled'
- svcs.append({"binary": svc['binary'], 'host': svc['host'],
- 'zone': svc['availability_zone'],
- 'status': active, 'state': art,
- 'updated_at': svc['updated_at']})
+ ret_fields = {'binary': svc['binary'], 'host': svc['host'],
+ 'zone': svc['availability_zone'],
+ 'status': active, 'state': art,
+ 'updated_at': svc['updated_at']}
+ if detailed:
+ ret_fields['disabled_reason'] = svc['disabled_reason']
+ svcs.append(ret_fields)
return {'services': svcs}
+ def _is_valid_as_reason(self, reason):
+ if not reason:
+ return False
+ try:
+ utils.check_string_length(reason.strip(), 'Disabled reason',
+ min_length=1, max_length=255)
+ except exception.InvalidInput:
+ return False
+
+ return True
+
@wsgi.serializers(xml=ServicesUpdateTemplate)
def update(self, req, id, body):
"""Enable/Disable scheduling for a service."""
context = req.environ['cinder.context']
authorize(context)
+ ext_loaded = self.ext_mgr.is_loaded('os-extended-services')
+ ret_val = {}
if id == "enable":
disabled = False
- elif id == "disable":
+ status = "enabled"
+ if ext_loaded:
+ ret_val['disabled_reason'] = None
+ elif (id == "disable" or
+ (id == "disable-log-reason" and ext_loaded)):
disabled = True
+ status = "disabled"
else:
raise webob.exc.HTTPNotFound("Unknown action")
except (TypeError, KeyError):
raise webob.exc.HTTPBadRequest()
+ ret_val['disabled'] = disabled
+ if id == "disable-log-reason" and ext_loaded:
+ reason = body.get('disabled_reason')
+ if not self._is_valid_as_reason(reason):
+ msg = _('Disabled reason contains invalid characters '
+ 'or is too long')
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ ret_val['disabled_reason'] = reason
+
# NOTE(uni): deprecating service request key, binary takes precedence
# Still keeping service key here for API compatibility sake.
service = body.get('service', '')
if not svc:
raise webob.exc.HTTPNotFound('Unknown service')
- db.service_update(context, svc['id'], {'disabled': disabled})
+ db.service_update(context, svc['id'], ret_val)
except exception.ServiceNotFound:
raise webob.exc.HTTPNotFound("service not found")
- status = id + 'd'
- return {'host': host,
- 'service': service,
- 'disabled': disabled,
- 'binary': binary,
- 'status': status}
+ ret_val.update({'host': host, 'service': service,
+ 'binary': binary, 'status': status})
+ return ret_val
class Services(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
- resource = extensions.ResourceExtension('os-services',
- ServiceController())
+ controller = ServiceController(self.ext_mgr)
+ resource = extensions.ResourceExtension('os-services', controller)
resources.append(resource)
return resources
# under the License.
+import webob.exc
+
from cinder.api.contrib import services
+from cinder.api import extensions
from cinder import context
from cinder import db
from cinder import exception
'id': 1,
'disabled': True,
'updated_at': datetime(2012, 10, 29, 13, 42, 2),
- 'created_at': datetime(2012, 9, 18, 2, 46, 27)},
+ 'created_at': datetime(2012, 9, 18, 2, 46, 27),
+ 'disabled_reason': 'test1'},
{'binary': 'cinder-volume',
'host': 'host1',
'availability_zone': 'cinder',
'id': 2,
'disabled': True,
'updated_at': datetime(2012, 10, 29, 13, 42, 5),
- 'created_at': datetime(2012, 9, 18, 2, 46, 27)},
+ 'created_at': datetime(2012, 9, 18, 2, 46, 27),
+ 'disabled_reason': 'test2'},
{'binary': 'cinder-scheduler',
'host': 'host2',
'availability_zone': 'cinder',
'id': 3,
'disabled': False,
'updated_at': datetime(2012, 9, 19, 6, 55, 34),
- 'created_at': datetime(2012, 9, 18, 2, 46, 28)},
+ 'created_at': datetime(2012, 9, 18, 2, 46, 28),
+ 'disabled_reason': ''},
{'binary': 'cinder-volume',
'host': 'host2',
'availability_zone': 'cinder',
'id': 4,
'disabled': True,
'updated_at': datetime(2012, 9, 18, 8, 3, 38),
- 'created_at': datetime(2012, 9, 18, 2, 46, 28)},
+ 'created_at': datetime(2012, 9, 18, 2, 46, 28),
+ 'disabled_reason': 'test4'},
]
self.stubs.Set(policy, "enforce", fake_policy_enforce)
self.context = context.get_admin_context()
- self.controller = services.ServiceController()
+ self.ext_mgr = extensions.ExtensionManager()
+ self.ext_mgr.extensions = {}
+ self.controller = services.ServiceController(self.ext_mgr)
def tearDown(self):
super(ServicesTest, self).tearDown()
'updated_at': datetime(2012, 9, 18, 8, 3, 38)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequest()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-scheduler',
+ 'host': 'host1', 'zone': 'cinder',
+ 'status': 'disabled', 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29, 13, 42, 2),
+ 'disabled_reason': 'test1'},
+ {'binary': 'cinder-volume',
+ 'host': 'host1', 'zone': 'cinder',
+ 'status': 'disabled', 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29, 13, 42, 5),
+ 'disabled_reason': 'test2'},
+ {'binary': 'cinder-scheduler', 'host': 'host2',
+ 'zone': 'cinder',
+ 'status': 'enabled', 'state': 'down',
+ 'updated_at': datetime(2012, 9, 19, 6, 55, 34),
+ 'disabled_reason': ''},
+ {'binary': 'cinder-volume', 'host': 'host2',
+ 'zone': 'cinder',
+ 'status': 'disabled', 'state': 'down',
+ 'updated_at': datetime(2012, 9, 18, 8, 3, 38),
+ 'disabled_reason': 'test4'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_list_with_host(self):
req = FakeRequestWithHost()
res_dict = self.controller.index(req)
13, 42, 5)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail_with_host(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequestWithHost()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-scheduler',
+ 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled', 'state': 'up',
+ 'updated_at': datetime(2012, 10,
+ 29, 13, 42, 2),
+ 'disabled_reason': 'test1'},
+ {'binary': 'cinder-volume', 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled', 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29,
+ 13, 42, 5),
+ 'disabled_reason': 'test2'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_list_with_service(self):
req = FakeRequestWithService()
res_dict = self.controller.index(req)
8, 3, 38)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail_with_service(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequestWithService()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-volume',
+ 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29,
+ 13, 42, 5),
+ 'disabled_reason': 'test2'},
+ {'binary': 'cinder-volume',
+ 'host': 'host2',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'down',
+ 'updated_at': datetime(2012, 9, 18,
+ 8, 3, 38),
+ 'disabled_reason': 'test4'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_list_with_binary(self):
req = FakeRequestWithBinary()
res_dict = self.controller.index(req)
8, 3, 38)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail_with_binary(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequestWithBinary()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-volume',
+ 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29,
+ 13, 42, 5),
+ 'disabled_reason': 'test2'},
+ {'binary': 'cinder-volume',
+ 'host': 'host2',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'down',
+ 'updated_at': datetime(2012, 9, 18,
+ 8, 3, 38),
+ 'disabled_reason': 'test4'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_list_with_host_service(self):
req = FakeRequestWithHostService()
res_dict = self.controller.index(req)
13, 42, 5)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail_with_host_service(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequestWithHostService()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-volume',
+ 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29,
+ 13, 42, 5),
+ 'disabled_reason': 'test2'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_list_with_host_binary(self):
req = FakeRequestWithHostBinary()
res_dict = self.controller.index(req)
13, 42, 5)}]}
self.assertEqual(res_dict, response)
+ def test_services_detail_with_host_binary(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = FakeRequestWithHostBinary()
+ res_dict = self.controller.index(req)
+
+ response = {'services': [{'binary': 'cinder-volume',
+ 'host': 'host1',
+ 'zone': 'cinder',
+ 'status': 'disabled',
+ 'state': 'up',
+ 'updated_at': datetime(2012, 10, 29,
+ 13, 42, 5),
+ 'disabled_reason': 'test2'}]}
+ self.assertEqual(res_dict, response)
+
def test_services_enable_with_service_key(self):
body = {'host': 'host1', 'service': 'cinder-volume'}
req = fakes.HTTPRequest.blank('/v1/fake/os-services/enable')
res_dict = self.controller.update(req, "disable", body)
self.assertEqual(res_dict['status'], 'disabled')
+
+ def test_services_disable_log_reason(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = (
+ fakes.HTTPRequest.blank('v1/fake/os-services/disable-log-reason'))
+ body = {'host': 'host1',
+ 'binary': 'cinder-scheduler',
+ 'disabled_reason': 'test-reason',
+ }
+ res_dict = self.controller.update(req, "disable-log-reason", body)
+
+ self.assertEqual(res_dict['status'], 'disabled')
+ self.assertEqual(res_dict['disabled_reason'], 'test-reason')
+
+ def test_services_disable_log_reason_none(self):
+ self.ext_mgr.extensions['os-extended-services'] = True
+ self.controller = services.ServiceController(self.ext_mgr)
+ req = (
+ fakes.HTTPRequest.blank('v1/fake/os-services/disable-log-reason'))
+ body = {'host': 'host1',
+ 'binary': 'cinder-scheduler',
+ 'disabled_reason': None,
+ }
+ self.assertRaises(webob.exc.HTTPBadRequest,
+ self.controller.update,
+ req, "disable-log-reason", body)
+
+ def test_invalid_reason_field(self):
+ reason = ' '
+ self.assertFalse(self.controller._is_valid_as_reason(reason))
+ reason = 'a' * 256
+ self.assertFalse(self.controller._is_valid_as_reason(reason))
+ reason = 'it\'s a valid reason.'
+ self.assertTrue(self.controller._is_valid_as_reason(reason))
+ reason = None
+ self.assertFalse(self.controller._is_valid_as_reason(reason))