summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorAndrea Rosa <andrea.rosa@hp.com>2013-02-20 10:10:04 +0000
committerMichael Still <mikal@stillhq.com>2013-06-12 11:27:25 +1000
commitc741e862fd35d28338d7966e03bf662c8fb65dac (patch)
treedbb6cd67e829fc5836d4ec1fc27b101e07c67fbe /nova/api
parentd7f898eab900de8f804285333e6fb921294520fc (diff)
downloadnova-c741e862fd35d28338d7966e03bf662c8fb65dac.tar.gz
nova-c741e862fd35d28338d7966e03bf662c8fb65dac.tar.xz
nova-c741e862fd35d28338d7966e03bf662c8fb65dac.zip
Give a way to save why a service has been disabled.
Implements blueprint record-reason-for-disabling-service We added a field to the service table to log a reason when a service has been disabled. We added a new API extension called os-extended-services. The new extension will extend the os-services extension adding: - A method for disabling a service and specify a reason for that. PUT /v2/{tenant_id}/os-services/disable-log-reason When the os-extended-extension is loaded the call: GET /V2/{tenant_id}/os-services will return the list of services with reason information it that exists. DocImpact Change-Id: I87a4affc45160796ff11c7b03e591e6aba73d62a
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/compute/contrib/extended_services.py11
-rw-r--r--nova/api/openstack/compute/contrib/services.py107
2 files changed, 92 insertions, 26 deletions
diff --git a/nova/api/openstack/compute/contrib/extended_services.py b/nova/api/openstack/compute/contrib/extended_services.py
new file mode 100644
index 000000000..f9f1a4d86
--- /dev/null
+++ b/nova/api/openstack/compute/contrib/extended_services.py
@@ -0,0 +1,11 @@
+from nova.api.openstack import extensions
+
+
+class Extended_services(extensions.ExtensionDescriptor):
+ """Extended services support."""
+
+ name = "ExtendedServices"
+ alias = "os-extended-services"
+ namespace = ("http://docs.openstack.org/compute/ext/"
+ "extended_services/api/v2")
+ updated = "2013-05-17T00:00:00-00:00"
diff --git a/nova/api/openstack/compute/contrib/services.py b/nova/api/openstack/compute/contrib/services.py
index bc5f60b64..3a637010a 100644
--- a/nova/api/openstack/compute/contrib/services.py
+++ b/nova/api/openstack/compute/contrib/services.py
@@ -23,6 +23,7 @@ from nova.api.openstack import xmlutil
from nova import compute
from nova import exception
from nova import servicegroup
+from nova import utils
authorize = extensions.extension_authorizer('compute', 'services')
CONF = cfg.CONF
@@ -39,6 +40,7 @@ class ServicesIndexTemplate(xmlutil.TemplateBuilder):
elem.set('status')
elem.set('state')
elem.set('updated_at')
+ elem.set('disabled_reason')
return xmlutil.MasterTemplate(root, 1)
@@ -49,6 +51,7 @@ class ServiceUpdateTemplate(xmlutil.TemplateBuilder):
root.set('host')
root.set('binary')
root.set('status')
+ root.set('disabled_reason')
return xmlutil.MasterTemplate(root, 1)
@@ -62,21 +65,20 @@ class ServiceUpdateDeserializer(wsgi.XMLDeserializer):
return service
service['host'] = service_node.getAttribute('host')
service['binary'] = service_node.getAttribute('binary')
+ service['disabled_reason'] = service_node.getAttribute(
+ 'disabled_reason')
return dict(body=service)
class ServiceController(object):
- def __init__(self):
+ def __init__(self, ext_mgr=None, *args, **kwargs):
self.host_api = compute.HostAPI()
self.servicegroup_api = servicegroup.API()
+ self.ext_mgr = ext_mgr
- @wsgi.serializers(xml=ServicesIndexTemplate)
- def index(self, req):
- """
- Return a list of all running services. Filter by host & service name.
- """
+ def _get_services(self, req):
context = req.environ['nova.context']
authorize(context)
services = self.host_api.service_get_all(
@@ -93,18 +95,49 @@ class ServiceController(object):
if binary:
services = [s for s in services if s['binary'] == binary]
+ return services
+
+ def _get_service_detail(self, svc, detailed):
+ alive = self.servicegroup_api.service_is_up(svc)
+ state = (alive and "up") or "down"
+ active = 'enabled'
+ if svc['disabled']:
+ active = 'disabled'
+ service_detail = {'binary': svc['binary'], 'host': svc['host'],
+ 'zone': svc['availability_zone'],
+ 'status': active, 'state': state,
+ 'updated_at': svc['updated_at']}
+ if detailed:
+ service_detail['disabled_reason'] = svc['disabled_reason']
+
+ return service_detail
+
+ def _get_services_list(self, req, detailed):
+ services = self._get_services(req)
svcs = []
for svc in services:
- alive = self.servicegroup_api.service_is_up(svc)
- art = (alive and "up") or "down"
- 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']})
- return {'services': svcs}
+ svcs.append(self._get_service_detail(svc, detailed))
+
+ return svcs
+
+ def _is_valid_as_reason(self, reason):
+ 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=ServicesIndexTemplate)
+ def index(self, req):
+ """
+ Return a list of all running services. Filter by host & service name.
+ """
+ detailed = self.ext_mgr.is_loaded('os-extended-services')
+ services = self._get_services_list(req, detailed)
+
+ return {'services': services}
@wsgi.deserializers(xml=ServiceUpdateDeserializer)
@wsgi.serializers(xml=ServiceUpdateTemplate)
@@ -113,28 +146,49 @@ class ServiceController(object):
context = req.environ['nova.context']
authorize(context)
+ ext_loaded = self.ext_mgr.is_loaded('os-extended-services')
if id == "enable":
disabled = False
- elif id == "disable":
+ status = "enabled"
+ elif (id == "disable" or
+ (id == "disable-log-reason" and ext_loaded)):
disabled = True
+ status = "disabled"
else:
- raise webob.exc.HTTPNotFound(_("Unknown action"))
-
- status = id + 'd'
-
+ raise webob.exc.HTTPNotFound("Unknown action")
try:
host = body['host']
binary = body['binary']
+ ret_value = {
+ 'service': {
+ 'host': host,
+ 'binary': binary,
+ 'status': status,
+ },
+ }
+ status_detail = {'disabled': disabled}
+ if id == "disable-log-reason":
+ reason = body['disabled_reason']
+ if not self._is_valid_as_reason(reason):
+ msg = _('Disabled reason contains invalid characters '
+ 'or is too long')
+ raise webob.exc.HTTPUnprocessableEntity(detail=msg)
+
+ status_detail['disabled_reason'] = reason
+ ret_value['service']['disabled_reason'] = reason
except (TypeError, KeyError):
- raise webob.exc.HTTPUnprocessableEntity()
+ msg = _('Invalid attribute in the request')
+ if 'host' in body and 'binary' in body:
+ msg = _('Missing disabled reason field')
+ raise webob.exc.HTTPUnprocessableEntity(detail=msg)
try:
svc = self.host_api.service_update(context, host, binary,
- {'disabled': disabled})
- except exception.ServiceNotFound as exc:
+ status_detail)
+ except exception.ServiceNotFound:
raise webob.exc.HTTPNotFound(_("Unknown service"))
- return {'service': {'host': host, 'binary': binary, 'status': status}}
+ return ret_value
class Services(extensions.ExtensionDescriptor):
@@ -148,6 +202,7 @@ class Services(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
resource = extensions.ResourceExtension('os-services',
- ServiceController())
+ ServiceController(self.ext_mgr))
+
resources.append(resource)
return resources