"Fossies" - the Fresh Open Source Software Archive

Member "glance-20.0.1/glance/common/store_utils.py" (12 Aug 2020, 7717 Bytes) of package /linux/misc/openstack/glance-20.0.1.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. For more information about "store_utils.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20.0.0_vs_20.0.1.

    1 #    Copyright 2014 IBM Corp.
    2 #
    3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 #    not use this file except in compliance with the License. You may obtain
    5 #    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, WITHOUT
   11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 #    License for the specific language governing permissions and limitations
   13 #    under the License.
   14 
   15 import sys
   16 
   17 import glance_store as store_api
   18 from oslo_config import cfg
   19 from oslo_log import log as logging
   20 from oslo_utils import encodeutils
   21 import six.moves.urllib.parse as urlparse
   22 
   23 import glance.db as db_api
   24 from glance.i18n import _LE
   25 from glance import scrubber
   26 
   27 LOG = logging.getLogger(__name__)
   28 
   29 CONF = cfg.CONF
   30 CONF.import_opt('use_user_token', 'glance.registry.client')
   31 
   32 RESTRICTED_URI_SCHEMAS = frozenset(['file', 'filesystem', 'swift+config'])
   33 
   34 
   35 def check_reserved_stores(enabled_stores):
   36     for store in enabled_stores:
   37         if store.startswith("os_glance_"):
   38             return True
   39     return False
   40 
   41 
   42 def safe_delete_from_backend(context, image_id, location):
   43     """
   44     Given a location, delete an image from the store and
   45     update location status to db.
   46 
   47     This function try to handle all known exceptions which might be raised
   48     by those calls on store and DB modules in its implementation.
   49 
   50     :param context: The request context
   51     :param image_id: The image identifier
   52     :param location: The image location entry
   53     """
   54 
   55     try:
   56         if CONF.enabled_backends:
   57             backend = location['metadata'].get('store')
   58             ret = store_api.delete(location['url'],
   59                                    backend,
   60                                    context=context)
   61         else:
   62             ret = store_api.delete_from_backend(location['url'],
   63                                                 context=context)
   64 
   65         location['status'] = 'deleted'
   66         if 'id' in location:
   67             db_api.get_api().image_location_delete(context, image_id,
   68                                                    location['id'], 'deleted')
   69         return ret
   70     except store_api.NotFound:
   71         msg = ("The image data for %(iid)s was not found in the store. "
   72                "The image record has been updated to reflect "
   73                "this." % {'iid': image_id})
   74         LOG.warn(msg)
   75     except store_api.StoreDeleteNotSupported as e:
   76         LOG.warn(encodeutils.exception_to_unicode(e))
   77     except store_api.UnsupportedBackend:
   78         exc_type = sys.exc_info()[0].__name__
   79         msg = (_LE('Failed to delete image %(image_id)s from store: %(exc)s') %
   80                dict(image_id=image_id, exc=exc_type))
   81         LOG.error(msg)
   82 
   83 
   84 def schedule_delayed_delete_from_backend(context, image_id, location):
   85     """
   86     Given a location, schedule the deletion of an image location and
   87     update location status to db.
   88 
   89     :param context: The request context
   90     :param image_id: The image identifier
   91     :param location: The image location entry
   92     """
   93 
   94     db_queue = scrubber.get_scrub_queue()
   95 
   96     if not CONF.use_user_token:
   97         context = None
   98 
   99     ret = db_queue.add_location(image_id, location)
  100     if ret:
  101         location['status'] = 'pending_delete'
  102         if 'id' in location:
  103             # NOTE(zhiyan): New added image location entry will has no 'id'
  104             # field since it has not been saved to DB.
  105             db_api.get_api().image_location_delete(context, image_id,
  106                                                    location['id'],
  107                                                    'pending_delete')
  108         else:
  109             db_api.get_api().image_location_add(context, image_id, location)
  110 
  111     return ret
  112 
  113 
  114 def delete_image_location_from_backend(context, image_id, location):
  115     """
  116     Given a location, immediately or schedule the deletion of an image
  117     location and update location status to db.
  118 
  119     :param context: The request context
  120     :param image_id: The image identifier
  121     :param location: The image location entry
  122     """
  123 
  124     deleted = False
  125     if CONF.delayed_delete:
  126         deleted = schedule_delayed_delete_from_backend(context,
  127                                                        image_id, location)
  128     if not deleted:
  129         # NOTE(zhiyan) If image metadata has not been saved to DB
  130         # such as uploading process failure then we can't use
  131         # location status mechanism to support image pending delete.
  132         safe_delete_from_backend(context, image_id, location)
  133 
  134 
  135 def validate_external_location(uri):
  136     """
  137     Validate if URI of external location are supported.
  138 
  139     Only over non-local store types are OK, i.e. Swift,
  140     HTTP. Note the absence of 'file://' for security reasons,
  141     see LP bug #942118, 1400966, 'swift+config://' is also
  142     absent for security reasons, see LP bug #1334196.
  143 
  144     :param uri: The URI of external image location.
  145     :returns: Whether given URI of external image location are OK.
  146     """
  147     if not uri:
  148         return False
  149 
  150     # TODO(zhiyan): This function could be moved to glance_store.
  151     # TODO(gm): Use a whitelist of allowed schemes
  152     scheme = urlparse.urlparse(uri).scheme
  153     known_schemes = store_api.get_known_schemes()
  154     if CONF.enabled_backends:
  155         known_schemes = store_api.get_known_schemes_for_multi_store()
  156 
  157     return (scheme in known_schemes and
  158             scheme not in RESTRICTED_URI_SCHEMAS)
  159 
  160 
  161 def _get_store_id_from_uri(uri):
  162     scheme = urlparse.urlparse(uri).scheme
  163     location_map = store_api.location.SCHEME_TO_CLS_BACKEND_MAP
  164     url_matched = False
  165     if scheme not in location_map:
  166         LOG.warning("Unknown scheme '%(scheme)s' found in uri '%(uri)s'", {
  167             'scheme': scheme, 'uri': uri})
  168         return
  169     for store in location_map[scheme]:
  170         store_instance = location_map[scheme][store]['store']
  171         url_prefix = store_instance.url_prefix
  172         if url_prefix and uri.startswith(url_prefix):
  173             url_matched = True
  174             break
  175 
  176     if url_matched:
  177         return u"%s" % store
  178     else:
  179         LOG.warning("Invalid location uri %s", uri)
  180         return
  181 
  182 
  183 def update_store_in_locations(image, image_repo):
  184     for loc in image.locations:
  185         if (not loc['metadata'].get(
  186                 'store') or loc['metadata'].get(
  187                 'store') not in CONF.enabled_backends):
  188             store_id = _get_store_id_from_uri(loc['url'])
  189             if store_id:
  190                 if 'store' in loc['metadata']:
  191                     old_store = loc['metadata']['store']
  192                     if old_store != store_id:
  193                         LOG.debug("Store '%(old)s' has changed to "
  194                                   "'%(new)s' by operator, updating "
  195                                   "the same in the location of image "
  196                                   "'%(id)s'", {'old': old_store,
  197                                                'new': store_id,
  198                                                'id': image.image_id})
  199 
  200                 loc['metadata']['store'] = store_id
  201                 image_repo.save(image)
  202 
  203 
  204 def get_updated_store_location(locations):
  205     for loc in locations:
  206         store_id = _get_store_id_from_uri(loc['url'])
  207         if store_id:
  208             loc['metadata']['store'] = store_id
  209 
  210     return locations
  211 
  212 
  213 def get_dir_separator():
  214     separator = ''
  215     staging_dir = "file://%s" % getattr(
  216         CONF, 'os_glance_staging_store').filesystem_store_datadir
  217     if not staging_dir.endswith('/'):
  218         separator = '/'
  219     return separator, staging_dir