"Fossies" - the Fresh Open Source Software Archive

Member "zaqar-10.0.0/zaqar/transport/wsgi/v1_1/pools.py" (13 May 2020, 7896 Bytes) of package /linux/misc/openstack/zaqar-10.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "pools.py": 9.0.0_vs_10.0.0.

    1 # Copyright (c) 2013 Rackspace Hosting, Inc.
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License");
    4 # you may not use this file except in compliance with the License.
    5 # You may obtain a copy of the License at
    6 #
    7 #    http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS,
   11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   12 # implied.
   13 # See the License for the specific language governing permissions and
   14 # limitations under the License.
   15 
   16 """pools: a resource to handle storage pool management
   17 
   18 A pool is added by an operator by interacting with the
   19 pooling-related endpoints. When specifying a pool, the
   20 following fields are required:
   21 
   22 ::
   23 
   24     {
   25         "name": string,
   26         "weight": integer,
   27         "uri": string::uri
   28     }
   29 
   30 Furthermore, depending on the underlying storage type of pool being
   31 registered, there is an optional field:
   32 ::
   33 
   34     {
   35         "options": {...}
   36     }
   37 """
   38 
   39 import falcon
   40 import jsonschema
   41 from oslo_log import log
   42 import six
   43 
   44 from zaqar.common.api.schemas import pools as schema
   45 from zaqar.common import utils as common_utils
   46 from zaqar.i18n import _
   47 from zaqar.storage import errors
   48 from zaqar.storage import utils as storage_utils
   49 from zaqar.transport import utils as transport_utils
   50 from zaqar.transport.wsgi import errors as wsgi_errors
   51 from zaqar.transport.wsgi import utils as wsgi_utils
   52 
   53 LOG = log.getLogger(__name__)
   54 
   55 
   56 class Listing(object):
   57     """A resource to list registered pools
   58 
   59     :param pools_controller: means to interact with storage
   60     """
   61 
   62     def __init__(self, pools_controller):
   63         self._ctrl = pools_controller
   64 
   65     def on_get(self, request, response, project_id):
   66         """Returns a pool listing as objects embedded in an object:
   67 
   68         ::
   69 
   70             {
   71                 "pools": [
   72                     {"href": "", "weight": 100, "uri": ""},
   73                     ...
   74                 ],
   75                 "links": [
   76                     {"href": "", "rel": "next"}
   77                 ]
   78             }
   79 
   80         :returns: HTTP | 200
   81         """
   82 
   83         LOG.debug(u'LIST pools')
   84 
   85         store = {}
   86         request.get_param('marker', store=store)
   87         request.get_param_as_int('limit', store=store)
   88         request.get_param_as_bool('detailed', store=store)
   89 
   90         cursor = self._ctrl.list(**store)
   91         pools = list(next(cursor))
   92 
   93         results = {'links': []}
   94 
   95         if pools:
   96             store['marker'] = next(cursor)
   97 
   98             for entry in pools:
   99                 entry['href'] = request.path + '/' + entry['name']
  100 
  101             results['links'] = [
  102                 {
  103                     'rel': 'next',
  104                     'href': request.path + falcon.to_query_str(store)
  105                 }
  106             ]
  107 
  108         results['pools'] = pools
  109 
  110         response.content_location = request.relative_uri
  111         response.body = transport_utils.to_json(results)
  112         response.status = falcon.HTTP_200
  113 
  114 
  115 class Resource(object):
  116     """A handler for individual pool.
  117 
  118     :param pools_controller: means to interact with storage
  119     """
  120 
  121     def __init__(self, pools_controller):
  122         self._ctrl = pools_controller
  123         validator_type = jsonschema.Draft4Validator
  124         self._validators = {
  125             'weight': validator_type(schema.patch_weight),
  126             'uri': validator_type(schema.patch_uri),
  127             'group': validator_type(schema.patch_uri),
  128             'options': validator_type(schema.patch_options),
  129             'create': validator_type(schema.create)
  130         }
  131 
  132     def on_get(self, request, response, project_id, pool):
  133         """Returns a JSON object for a single pool entry:
  134 
  135         ::
  136 
  137             {"weight": 100, "uri": "", options: {...}}
  138 
  139         :returns: HTTP | [200, 404]
  140         """
  141 
  142         LOG.debug(u'GET pool - name: %s', pool)
  143         data = None
  144         detailed = request.get_param_as_bool('detailed') or False
  145 
  146         try:
  147             data = self._ctrl.get(pool, detailed)
  148 
  149         except errors.PoolDoesNotExist as ex:
  150             LOG.debug(ex)
  151             raise wsgi_errors.HTTPNotFound(six.text_type(ex))
  152 
  153         data['href'] = request.path
  154 
  155         response.body = transport_utils.to_json(data)
  156 
  157     def on_put(self, request, response, project_id, pool):
  158         """Registers a new pool. Expects the following input:
  159 
  160         ::
  161 
  162             {"weight": 100, "uri": ""}
  163 
  164         An options object may also be provided.
  165 
  166         :returns: HTTP | [201, 204]
  167         """
  168 
  169         LOG.debug(u'PUT pool - name: %s', pool)
  170 
  171         conf = self._ctrl.driver.conf
  172         data = wsgi_utils.load(request)
  173         wsgi_utils.validate(self._validators['create'], data)
  174         if not storage_utils.can_connect(data['uri'], conf=conf):
  175             raise wsgi_errors.HTTPBadRequestBody(
  176                 'cannot connect to %s' % data['uri']
  177             )
  178         try:
  179             self._ctrl.create(pool, weight=data['weight'],
  180                               uri=data['uri'],
  181                               options=data.get('options', {}))
  182             response.status = falcon.HTTP_201
  183             response.location = request.path
  184         except errors.PoolCapabilitiesMismatch as e:
  185             title = _(u'Unable to create pool')
  186             LOG.exception(title)
  187             raise falcon.HTTPBadRequest(title, six.text_type(e))
  188         except errors.PoolAlreadyExists as e:
  189             LOG.exception('Pool "%s" already exists', pool)
  190             raise wsgi_errors.HTTPConflict(six.text_type(e))
  191 
  192     def on_delete(self, request, response, project_id, pool):
  193         """Deregisters a pool.
  194 
  195         :returns: HTTP | [204, 403]
  196         """
  197 
  198         LOG.debug(u'DELETE pool - name: %s', pool)
  199 
  200         try:
  201             self._ctrl.delete(pool)
  202         except errors.PoolInUseByFlavor as ex:
  203             title = _(u'Unable to delete')
  204             description = _(u'This pool is used by flavors {flavor}; '
  205                             u'It cannot be deleted.')
  206             description = description.format(flavor=ex.flavor)
  207             LOG.exception(description)
  208             raise falcon.HTTPForbidden(title, description)
  209 
  210         response.status = falcon.HTTP_204
  211 
  212     def on_patch(self, request, response, project_id, pool):
  213         """Allows one to update a pool's weight, uri, and/or options.
  214 
  215         This method expects the user to submit a JSON object
  216         containing at least one of: 'uri', 'weight', 'group', 'options'. If
  217         none are found, the request is flagged as bad. There is also
  218         strict format checking through the use of
  219         jsonschema. Appropriate errors are returned in each case for
  220         badly formatted input.
  221 
  222         :returns: HTTP | 200,400
  223         """
  224 
  225         LOG.debug(u'PATCH pool - name: %s', pool)
  226         data = wsgi_utils.load(request)
  227 
  228         EXPECT = ('weight', 'uri', 'options')
  229         if not any([(field in data) for field in EXPECT]):
  230             LOG.debug(u'PATCH pool, bad params')
  231             raise wsgi_errors.HTTPBadRequestBody(
  232                 'One of `uri`, `weight`,or `options` needs '
  233                 'to be specified'
  234             )
  235 
  236         for field in EXPECT:
  237             wsgi_utils.validate(self._validators[field], data)
  238 
  239         conf = self._ctrl.driver.conf
  240         if 'uri' in data and not storage_utils.can_connect(data['uri'],
  241                                                            conf=conf):
  242             raise wsgi_errors.HTTPBadRequestBody(
  243                 'cannot connect to %s' % data['uri']
  244             )
  245         fields = common_utils.fields(data, EXPECT,
  246                                      pred=lambda v: v is not None)
  247 
  248         try:
  249             self._ctrl.update(pool, **fields)
  250         except errors.PoolDoesNotExist as ex:
  251             LOG.exception('Pool "%s" does not exist', pool)
  252             raise wsgi_errors.HTTPNotFound(six.text_type(ex))