Coverage for manila/api/v2/services.py: 83%
106 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright 2012 IBM Corp.
2# Copyright (c) 2015 Mirantis inc.
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
17import http.client as http_client
18from oslo_utils import strutils
19import webob.exc
21from manila.api.openstack import wsgi
22from manila.api.views import services as services_views
23from manila import db
24from manila import exception
25from manila.i18n import _
26from manila.services import api as service_api
29class ServiceMixin(object):
30 """The Services API controller common logic.
32 Mixin class that should be inherited by Services API controllers,
33 which are used for different API URLs and microversions.
34 """
36 resource_name = "service"
37 _view_builder_class = services_views.ViewBuilder
39 @wsgi.Controller.authorize("index")
40 def _index(self, req, support_ensure_shares=False):
41 """Return a list of all running services."""
43 context = req.environ['manila.context']
44 all_services = db.service_get_all(context)
46 services = []
47 for service in all_services:
48 service_data = {
49 'id': service['id'],
50 'binary': service['binary'],
51 'host': service['host'],
52 'zone': service['availability_zone']['name'],
53 'status': 'disabled' if service['disabled'] else 'enabled',
54 'disabled_reason': service.get('disabled_reason'),
55 'state': service['state'],
56 'updated_at': service['updated_at'],
57 }
58 if support_ensure_shares: 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true
59 service_data['ensuring'] = service['ensuring']
60 services.append(service_data)
62 search_opts = [
63 'host',
64 'binary',
65 'zone',
66 'state',
67 'status',
68 ]
69 for search_opt in search_opts:
70 if search_opt in req.GET:
71 value = req.GET[search_opt]
72 services = [s for s in services if s[search_opt] == value]
73 if len(services) == 0: 73 ↛ 74line 73 didn't jump to line 74 because the condition on line 73 was never true
74 break
76 return self._view_builder.detail_list(req, services)
78 @wsgi.Controller.authorize("update")
79 def _update(self, req, id, body, support_disabled_reason=True):
80 """Enable/Disable scheduling for a service."""
81 context = req.environ['manila.context']
82 update_dict = {}
84 if id == "enable":
85 data = {'disabled': False}
86 if support_disabled_reason: 86 ↛ 87line 86 didn't jump to line 87 because the condition on line 86 was never true
87 update_dict['disabled_reason'] = None
88 elif id == "disable": 88 ↛ 109line 88 didn't jump to line 109 because the condition on line 88 was always true
89 data = {'disabled': True}
90 disabled_reason = body.get('disabled_reason')
91 if disabled_reason and not support_disabled_reason: 91 ↛ 92line 91 didn't jump to line 92 because the condition on line 91 was never true
92 msg = _("'disabled_reason' option is not supported by this "
93 "microversion. Use 2.83 or greater microversion to "
94 "be able to set 'disabled_reason'.")
95 raise webob.exc.HTTPBadRequest(explanation=msg)
96 if disabled_reason: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true
97 try:
98 strutils.check_string_length(disabled_reason.strip(),
99 name='disabled_reason',
100 min_length=1,
101 max_length=255)
102 except (ValueError, TypeError):
103 msg = _('Disabled reason contains invalid characters '
104 'or is too long')
105 raise webob.exc.HTTPBadRequest(explanation=msg)
106 update_dict['disabled_reason'] = disabled_reason.strip()
107 data['disabled_reason'] = disabled_reason.strip()
108 else:
109 raise webob.exc.HTTPNotFound("Unknown action '%s'" % id)
111 try:
112 data['host'] = body['host']
113 data['binary'] = body['binary']
114 except (TypeError, KeyError):
115 raise webob.exc.HTTPBadRequest()
117 svc = db.service_get_by_args(context, data['host'], data['binary'])
118 update_dict['disabled'] = data['disabled']
120 db.service_update(context, svc['id'], update_dict)
121 data['status'] = 'disabled' if id == "disable" else 'enabled'
123 return self._view_builder.summary(req, data)
126class ServiceControllerLegacy(ServiceMixin, wsgi.Controller):
127 """Deprecated Services API controller.
129 Used from microversions 2.0 to 2.6. Registered under deprecated API URL
130 'os-services'.
131 """
133 @wsgi.Controller.api_version('1.0', '2.6')
134 def index(self, req):
135 return self._index(req)
137 @wsgi.Controller.api_version('1.0', '2.6')
138 def update(self, req, id, body):
139 return self._update(req, id, body, support_disabled_reason=False)
142class ServiceController(ServiceMixin, wsgi.Controller):
143 """Services API controller.
145 Used from microversion 2.7. Registered under API URL 'services'.
146 """
148 def __init__(self):
149 super().__init__()
150 self.service_api = service_api.API()
152 @wsgi.Controller.api_version('2.7', '2.85')
153 def index(self, req):
154 return self._index(req)
156 @wsgi.Controller.api_version('2.86') # noqa
157 def index(self, req): # pylint: disable=function-redefined # noqa F811
158 return self._index(req, support_ensure_shares=True)
160 @wsgi.Controller.api_version('2.7', '2.82')
161 def update(self, req, id, body):
162 return self._update(req, id, body, support_disabled_reason=False)
164 @wsgi.Controller.api_version('2.83') # noqa
165 def update(self, req, id, body): # pylint: disable=function-redefined # noqa F811
166 return self._update(req, id, body)
168 @wsgi.Controller.api_version('2.86')
169 @wsgi.Controller.authorize
170 def ensure_shares(self, req, body):
171 """Starts ensure shares for a given manila-share binary."""
172 context = req.environ['manila.context']
174 host = body.get('host', None)
175 if not host:
176 raise webob.exc.HTTPBadRequest('Missing host parameter.')
178 try:
179 # The only binary supported is Manila share.
180 service = db.service_get_by_args(context, host, 'manila-share')
181 except exception.NotFound:
182 raise webob.exc.HTTPNotFound(
183 "manila-share binary for '%s' host not found" % id
184 )
186 try:
187 self.service_api.ensure_shares(context, service, host)
188 except webob.exc.HTTPConflict:
189 raise
191 return webob.Response(status_int=http_client.ACCEPTED)
194def create_resource_legacy():
195 return wsgi.Resource(ServiceControllerLegacy())
198def create_resource():
199 return wsgi.Resource(ServiceController())