From 74094b51b71af30df6ca40c5d3f6bca47b48caeb Mon Sep 17 00:00:00 2001 From: Fei Long Wang Date: Wed, 16 Nov 2016 14:33:58 +1300 Subject: [PATCH] Avoid deleting location data if current image is not the owner Change-Id: Ia949818a75b9162b8cace07a24f6276e3492a503 --- glance/db/simple/api.py | 33 +++++++++++++++++++++++++++++++++ glance/db/sqlalchemy/api.py | 25 +++++++++++++++++++++++++ glance/location.py | 20 ++++++++++++++++---- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/glance/db/simple/api.py b/glance/db/simple/api.py index fd3d5ee..7319382 100644 --- a/glance/db/simple/api.py +++ b/glance/db/simple/api.py @@ -619,6 +619,39 @@ def image_location_delete(context, image_id, location_id, status, raise exception.NotFound(msg) +def image_locations_with_url(context, location): + locations = [] + try: + session = session or get_session() + short_rbd_locations = [] + if location["url"].startswith("rbd://"): + pieces = location['url'][len("rbd://"):].split('/') + # NOTE(flwang): We can deduct the short rbd location based on the + # long one, but it's impossible to do it in reverse since there's + # no way to know the FSID. + if len(pieces) == 4: + for loc in DATA['locations']: + if loc['url'] == "rbd://" + pieces[2]: + short_rbd_locations.append(loc) + + for loc in DATA['locations']: + if loc['url'] == location['url']: + locations.append(loc) + keyfn = lambda x: (x[sort_key] if x[sort_key] is not None else '', + x['created_at'], x['id']) + + if short_rbd_locations: + locations.extend(short_rbd_locations) + + locations.sort(key=keyfn) + return locations + except sa_orm.exc.NoResultFound: + msg = (_("No location found with url %(loc)s") % + dict(loc=location['url'])) + LOG.warn(msg) + return locations + + def _image_locations_set(context, image_id, locations): # NOTE(zhiyan): 1. Remove records from DB for deleted locations used_loc_ids = [loc['id'] for loc in locations if loc.get('id')] diff --git a/glance/db/sqlalchemy/api.py b/glance/db/sqlalchemy/api.py index 77ab684..9d548c1 100644 --- a/glance/db/sqlalchemy/api.py +++ b/glance/db/sqlalchemy/api.py @@ -938,6 +938,31 @@ def image_location_delete(context, image_id, location_id, status, raise exception.NotFound(msg) +def image_locations_with_url(context, location, session=None): + try: + session = session or get_session() + short_rbd_locations = [] + if location["url"].startswith("rbd://"): + pieces = location['url'][len("rbd://"):].split('/') + # NOTE(flwang): We can deduct the short rbd location based on the + # long one, but it's impossible to do it in reverse since there's + # no way to know the FSID. + if len(pieces) == 4: + short_rbd_locations = session.query(models.ImageLocation).filter_by( + value="rbd://" + pieces[2]).order_by(models.ImageLocation.created_at).all() + + locations = session.query(models.ImageLocation).filter_by( + value=location['url']).order_by(models.ImageLocation.created_at).all() + if short_rbd_locations: + locations.extend(short_rbd_locations) + return locations + except sa_orm.exc.NoResultFound: + msg = (_("No location found with url %(loc)s") % + dict(loc=location['url'])) + LOG.warn(msg) + return [] + + def _image_locations_set(context, image_id, locations, session=None): # NOTE(zhiyan): 1. Remove records from DB for deleted locations session = session or get_session() diff --git a/glance/location.py b/glance/location.py index f7f2250..9ebc6c0 100644 --- a/glance/location.py +++ b/glance/location.py @@ -400,10 +400,22 @@ class ImageProxy(glance.domain.proxy.Image): self.image.delete() if self.image.locations: for location in self.image.locations: - self.store_utils.delete_image_location_from_backend( - self.context, - self.image.image_id, - location) + locs = self.image.db_api.image_locations_with_url(self.context, + location) + # NOTE(flwang): 1. If current image is the "owner" of this + # location, the location can be deleted, no matter if there + # is another image using it. 2. If current image is not the + # "owner" of this location, the location's back end data can't + # be deleted. + # The locations list returned by function + # "image_locations_with_url" are sorted by the creation time + # of the location, because technically, the location owner is + # the image who created the location at the first time. + if len(locs) > 0 and locs[0].image_id == self.image.image_id: + self.store_utils.delete_image_location_from_backend( + self.context, + self.image.image_id, + location) def set_data(self, data, size=None): if size is None: -- 1.9.1