Fix scrubber not scrubbing with swift backend
It seems the fix for bug #1052537 was incomplete, as I have found glance-scrubber is still not completing it's work when run against a swift backend. With more investigation it seems the proper way to execute a starmap/imap is to consume it within a loop. This can be replicated by taking the simple web crawler example from http://eventlet.net/doc/ and removing the for loop from around the imap call. Also: * Add new test to check this failure against Swift (respects the GLANCE_TEST_SWIFT_CONF environment variable). Fixes bug 1056127 Change-Id: I2d289666b99f3216a6d424dbbacbd2d10899263d
This commit is contained in:
parent
3e532bb746
commit
d032ad5d41
@ -121,10 +121,9 @@ class Scrubber(object):
|
|||||||
delete_work.append((id, uri, now))
|
delete_work.append((id, uri, now))
|
||||||
|
|
||||||
LOG.info(_("Deleting %s images") % len(delete_work))
|
LOG.info(_("Deleting %s images") % len(delete_work))
|
||||||
pool.starmap(self._delete, delete_work)
|
# NOTE(bourke): The starmap must be iterated to do work
|
||||||
# NOTE(bourke): When not running as a daemon, a slight pause is needed
|
for job in pool.starmap(self._delete, delete_work):
|
||||||
# to allow the starmap to begin it's work.
|
pass
|
||||||
eventlet.sleep(0.1)
|
|
||||||
|
|
||||||
if self.cleanup:
|
if self.cleanup:
|
||||||
self._cleanup(pool)
|
self._cleanup(pool)
|
||||||
@ -187,7 +186,9 @@ class Scrubber(object):
|
|||||||
now))
|
now))
|
||||||
|
|
||||||
LOG.info(_("Deleting %s images") % len(delete_work))
|
LOG.info(_("Deleting %s images") % len(delete_work))
|
||||||
pool.starmap(self._delete, delete_work)
|
# NOTE(bourke): The starmap must be iterated to do work
|
||||||
|
for job in pool.starmap(self._delete, delete_work):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def read_queue_file(file_path):
|
def read_queue_file(file_path):
|
||||||
|
@ -193,10 +193,10 @@ class ApiServer(Server):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, test_dir, port, policy_file, delayed_delete=False,
|
def __init__(self, test_dir, port, policy_file, delayed_delete=False,
|
||||||
pid_file=None):
|
pid_file=None, **kwargs):
|
||||||
super(ApiServer, self).__init__(test_dir, port)
|
super(ApiServer, self).__init__(test_dir, port)
|
||||||
self.server_name = 'api'
|
self.server_name = 'api'
|
||||||
self.default_store = 'file'
|
self.default_store = kwargs.get("default_store", "file")
|
||||||
self.key_file = ""
|
self.key_file = ""
|
||||||
self.cert_file = ""
|
self.cert_file = ""
|
||||||
self.metadata_encryption_key = "012345678901234567890123456789ab"
|
self.metadata_encryption_key = "012345678901234567890123456789ab"
|
||||||
@ -209,10 +209,15 @@ class ApiServer(Server):
|
|||||||
self.s3_store_secret_key = ""
|
self.s3_store_secret_key = ""
|
||||||
self.s3_store_bucket = ""
|
self.s3_store_bucket = ""
|
||||||
self.s3_store_bucket_url_format = ""
|
self.s3_store_bucket_url_format = ""
|
||||||
self.swift_store_auth_address = ""
|
self.swift_store_auth_version = kwargs.get("swift_store_auth_version",
|
||||||
self.swift_store_user = ""
|
"2")
|
||||||
self.swift_store_key = ""
|
self.swift_store_auth_address = kwargs.get("swift_store_auth_address",
|
||||||
self.swift_store_container = ""
|
"")
|
||||||
|
self.swift_store_user = kwargs.get("swift_store_user", "")
|
||||||
|
self.swift_store_key = kwargs.get("swift_store_key", "")
|
||||||
|
self.swift_store_container = kwargs.get("swift_store_container", "")
|
||||||
|
self.swift_store_create_container_on_put = kwargs.get(
|
||||||
|
"swift_store_create_container_on_put", "True")
|
||||||
self.swift_store_large_object_size = 5 * 1024
|
self.swift_store_large_object_size = 5 * 1024
|
||||||
self.swift_store_large_object_chunk_size = 200
|
self.swift_store_large_object_chunk_size = 200
|
||||||
self.swift_store_multi_tenant = False
|
self.swift_store_multi_tenant = False
|
||||||
@ -255,10 +260,12 @@ s3_store_access_key = %(s3_store_access_key)s
|
|||||||
s3_store_secret_key = %(s3_store_secret_key)s
|
s3_store_secret_key = %(s3_store_secret_key)s
|
||||||
s3_store_bucket = %(s3_store_bucket)s
|
s3_store_bucket = %(s3_store_bucket)s
|
||||||
s3_store_bucket_url_format = %(s3_store_bucket_url_format)s
|
s3_store_bucket_url_format = %(s3_store_bucket_url_format)s
|
||||||
|
swift_store_auth_version = %(swift_store_auth_version)s
|
||||||
swift_store_auth_address = %(swift_store_auth_address)s
|
swift_store_auth_address = %(swift_store_auth_address)s
|
||||||
swift_store_user = %(swift_store_user)s
|
swift_store_user = %(swift_store_user)s
|
||||||
swift_store_key = %(swift_store_key)s
|
swift_store_key = %(swift_store_key)s
|
||||||
swift_store_container = %(swift_store_container)s
|
swift_store_container = %(swift_store_container)s
|
||||||
|
swift_store_create_container_on_put = %(swift_store_create_container_on_put)s
|
||||||
swift_store_large_object_size = %(swift_store_large_object_size)s
|
swift_store_large_object_size = %(swift_store_large_object_size)s
|
||||||
swift_store_large_object_chunk_size = %(swift_store_large_object_chunk_size)s
|
swift_store_large_object_chunk_size = %(swift_store_large_object_chunk_size)s
|
||||||
swift_store_multi_tenant = %(swift_store_multi_tenant)s
|
swift_store_multi_tenant = %(swift_store_multi_tenant)s
|
||||||
@ -404,7 +411,7 @@ class ScrubberDaemon(Server):
|
|||||||
Server object that starts/stops/manages the Scrubber server
|
Server object that starts/stops/manages the Scrubber server
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, test_dir, daemon=False):
|
def __init__(self, test_dir, daemon=False, **kwargs):
|
||||||
# NOTE(jkoelker): Set the port to 0 since we actually don't listen
|
# NOTE(jkoelker): Set the port to 0 since we actually don't listen
|
||||||
super(ScrubberDaemon, self).__init__(test_dir, 0)
|
super(ScrubberDaemon, self).__init__(test_dir, 0)
|
||||||
self.server_name = 'scrubber'
|
self.server_name = 'scrubber'
|
||||||
@ -414,6 +421,13 @@ class ScrubberDaemon(Server):
|
|||||||
"scrubber")
|
"scrubber")
|
||||||
self.pid_file = os.path.join(self.test_dir, "scrubber.pid")
|
self.pid_file = os.path.join(self.test_dir, "scrubber.pid")
|
||||||
self.log_file = os.path.join(self.test_dir, "scrubber.log")
|
self.log_file = os.path.join(self.test_dir, "scrubber.log")
|
||||||
|
self.swift_store_auth_address = kwargs.get("swift_store_auth_address",
|
||||||
|
"")
|
||||||
|
self.swift_store_user = kwargs.get("swift_store_user", "")
|
||||||
|
self.swift_store_key = kwargs.get("swift_store_key", "")
|
||||||
|
self.swift_store_container = kwargs.get("swift_store_container", "")
|
||||||
|
self.swift_store_auth_version = kwargs.get("swift_store_auth_version",
|
||||||
|
"2")
|
||||||
self.conf_base = """[DEFAULT]
|
self.conf_base = """[DEFAULT]
|
||||||
verbose = %(verbose)s
|
verbose = %(verbose)s
|
||||||
debug = %(debug)s
|
debug = %(debug)s
|
||||||
@ -423,6 +437,11 @@ wakeup_time = 2
|
|||||||
scrubber_datadir = %(scrubber_datadir)s
|
scrubber_datadir = %(scrubber_datadir)s
|
||||||
registry_host = 127.0.0.1
|
registry_host = 127.0.0.1
|
||||||
registry_port = %(registry_port)s
|
registry_port = %(registry_port)s
|
||||||
|
swift_store_auth_address = %(swift_store_auth_address)s
|
||||||
|
swift_store_user = %(swift_store_user)s
|
||||||
|
swift_store_key = %(swift_store_key)s
|
||||||
|
swift_store_container = %(swift_store_container)s
|
||||||
|
swift_store_auth_version = %(swift_store_auth_version)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2011 OpenStack, LLC
|
# Copyright 2011-2012 OpenStack, LLC
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -16,11 +16,14 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import httplib2
|
import httplib2
|
||||||
|
import nose
|
||||||
|
|
||||||
from glance.tests import functional
|
from glance.tests import functional
|
||||||
|
from glance.tests.functional.store.test_swift import parse_config
|
||||||
|
from glance.tests.functional.store.test_swift import read_config
|
||||||
from glance.tests.utils import execute
|
from glance.tests.utils import execute
|
||||||
|
|
||||||
|
|
||||||
@ -149,3 +152,77 @@ class TestScrubber(functional.FunctionalTest):
|
|||||||
self.fail('image was never scrubbed')
|
self.fail('image was never scrubbed')
|
||||||
|
|
||||||
self.stop_servers()
|
self.stop_servers()
|
||||||
|
|
||||||
|
def test_scrubber_app_against_swift(self):
|
||||||
|
"""
|
||||||
|
test that the glance-scrubber script runs successfully against a swift
|
||||||
|
backend when not in daemon mode
|
||||||
|
"""
|
||||||
|
config_path = os.environ.get('GLANCE_TEST_SWIFT_CONF')
|
||||||
|
if not config_path:
|
||||||
|
msg = "GLANCE_TEST_SWIFT_CONF environ not set."
|
||||||
|
raise nose.SkipTest(msg)
|
||||||
|
|
||||||
|
raw_config = read_config(config_path)
|
||||||
|
swift_config = parse_config(raw_config)
|
||||||
|
|
||||||
|
self.cleanup()
|
||||||
|
self.start_servers(delayed_delete=True, daemon=False,
|
||||||
|
default_store='swift', **swift_config)
|
||||||
|
|
||||||
|
# add an image
|
||||||
|
headers = {
|
||||||
|
'x-image-meta-name': 'test_image',
|
||||||
|
'x-image-meta-is_public': 'true',
|
||||||
|
'x-image-meta-disk_format': 'raw',
|
||||||
|
'x-image-meta-container_format': 'ovf',
|
||||||
|
'content-type': 'application/octet-stream',
|
||||||
|
}
|
||||||
|
path = "http://%s:%d/v1/images" % ("127.0.0.1", self.api_port)
|
||||||
|
http = httplib2.Http()
|
||||||
|
response, content = http.request(path, 'POST', body='XXX',
|
||||||
|
headers=headers)
|
||||||
|
# ensure the request was successful and the image is active
|
||||||
|
self.assertEqual(response.status, 201)
|
||||||
|
image = json.loads(content)['image']
|
||||||
|
self.assertEqual('active', image['status'])
|
||||||
|
image_id = image['id']
|
||||||
|
|
||||||
|
# delete the image
|
||||||
|
path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", self.api_port,
|
||||||
|
image_id)
|
||||||
|
http = httplib2.Http()
|
||||||
|
response, content = http.request(path, 'DELETE')
|
||||||
|
self.assertEqual(response.status, 200)
|
||||||
|
|
||||||
|
# ensure the image is marked pending delete
|
||||||
|
response, content = http.request(path, 'HEAD')
|
||||||
|
self.assertEqual(response.status, 200)
|
||||||
|
self.assertEqual('pending_delete', response['x-image-meta-status'])
|
||||||
|
|
||||||
|
# wait for the scrub time on the image to pass
|
||||||
|
time.sleep(self.api_server.scrub_time)
|
||||||
|
|
||||||
|
# call the scrubber to scrub images
|
||||||
|
cmd = ("bin/glance-scrubber --config-file %s" %
|
||||||
|
self.scrubber_daemon.conf_file_name)
|
||||||
|
exitcode, out, err = execute(cmd, raise_error=False)
|
||||||
|
self.assertEqual(0, exitcode)
|
||||||
|
|
||||||
|
# ensure the image has been successfully deleted
|
||||||
|
# NOTE(jkoelker) The build servers sometimes take longer than
|
||||||
|
# 15 seconds to scrub. Give it up to 5 min, checking
|
||||||
|
# checking every 15 seconds. When/if it flips to
|
||||||
|
# deleted, bail immediately.
|
||||||
|
for _ in xrange(3):
|
||||||
|
time.sleep(5)
|
||||||
|
response, content = http.request(path, 'HEAD')
|
||||||
|
if (response['x-image-meta-status'] == 'deleted' and
|
||||||
|
response['x-image-meta-deleted'] == 'True'):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.fail('image was never scrubbed')
|
||||||
|
|
||||||
|
self.stop_servers()
|
||||||
|
Loading…
Reference in New Issue
Block a user