def test_add_list_delete_list(self): """ We test the following: 0. Verify no public images in index 1. Add a public image with a location attr and no image data 2. Check that image exists in index 3. Delete the image 4. Verify no longer in index """ self.cleanup() self.start_servers() api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Add public image cmd = "bin/glance --port=%d add is_public=True name=MyImage" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('Added new image with ID: 1', out.strip()) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) self.assertTrue('MyImage' in lines[0]) # 3. Delete the image cmd = "bin/glance --port=%d --force delete 1" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('Deleted image 1', out.strip()) # 4. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) self.stop_servers()
def test_exception_not_eaten_from_registry_to_api(self): """ A test for LP bug #704854 -- Exception thrown by registry server is consumed by API server. We start both servers daemonized. We then use curl to try adding an image that does not meet validation requirements on the registry server and test that the error returned from the API server to curl is appropriate """ self.cleanup() self.start_servers() api_port = self.api_port cmd = "curl -g http://127.0.0.1:%d/v1/images" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('{"images": []}', out.strip()) cmd = ("curl -X POST -H 'Content-Type: application/octet-stream' " "-H 'X-Image-Meta-Name: ImageName' " "-H 'X-Image-Meta-Disk-Format: Invalid' " "http://127.0.0.1:%d/v1/images" % api_port) ignored, out, err = execute(cmd) self.assertTrue('Invalid disk format' in out, "Could not find 'Invalid disk format' " "in output: %s" % out) self.stop_servers()
def start_servers(self, **kwargs): """ Starts the authentication and admin servers (keystone-auth and keystone-admin) on unused ports, in addition to the Glance API and Registry servers. Any kwargs passed to this method will override the configuration value in the conf file used in starting the servers. """ # Start with the Glance servers super(KeystoneTests, self).start_servers(**kwargs) # Set up the data store keystone_conf = self.auth_server.write_conf(**kwargs) datafile = os.path.join(os.path.dirname(__file__), "data", "keystone_data.py") execute("python %s -c %s" % (datafile, keystone_conf)) # Start keystone-auth exitcode, out, err = self.auth_server.start(**kwargs) self.assertEqual(0, exitcode, "Failed to spin up the Auth server. " "Got: %s" % err) self.assertTrue("Starting keystone-auth with" in out) # Now keystone-admin exitcode, out, err = self.admin_server.start(**kwargs) self.assertEqual(0, exitcode, "Failed to spin up the Admin server. " "Got: %s" % err) self.assertTrue("Starting keystone-admin with" in out) self.wait_for_keystone_servers()
def _setup_database(self): sql_connection = 'sqlite:////%s/tests.sqlite' % self.test_dir self.config(connection=sql_connection, group='database') glance.db.sqlalchemy.api.clear_db_env() glance_db_env = 'GLANCE_DB_TEST_SQLITE_FILE' if glance_db_env in os.environ: # use the empty db created and cached as a tempfile # instead of spending the time creating a new one db_location = os.environ[glance_db_env] test_utils.execute('cp %s %s/tests.sqlite' % (db_location, self.test_dir)) else: glance.db.sqlalchemy.migration.db_sync() # copy the clean db to a temp location so that it # can be reused for future tests (osf, db_location) = tempfile.mkstemp() os.close(osf) test_utils.execute('cp %s/tests.sqlite %s' % (self.test_dir, db_location)) os.environ[glance_db_env] = db_location # cleanup the temp file when the test suite is # complete def _delete_cached_db(): try: os.remove(os.environ[glance_db_env]) except Exception: glance_tests.logger.exception( "Error cleaning up the file %s" % os.environ[glance_db_env]) atexit.register(_delete_cached_db)
def test_api_treats_size_as_a_normal_property(self): """ A test for LP bug #825024 -- glance client currently treats size as a normal property. """ self.cleanup() self.start_servers() # 1. POST /images with public image named Image1 # attribute and no custom properties. Verify a 200 OK is returned with tempfile.NamedTemporaryFile() as image_file: image_file.write("XXX") image_file.flush() image_file_name = image_file.name cmd = "bin/glance --port=%d add is_public=True name=MyImage "\ "size=12345 < %s" % (self.api_port, image_file_name) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue('Found non-settable field size. Removing.' in out) self.assertTrue('Added new image with ID: 1' in out) # 2. Verify image added as public image cmd = "bin/glance --port=%d show %d" % (self.api_port, 1) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertFalse("12345" in out) self.stop_servers()
def test_killed_image_not_in_index(self): """ We test conditions that produced LP Bug #768969, where an image in the 'killed' status is displayed in the output of glance index, and the status column is not displayed in the output of glance show <ID>. Start servers with Swift backend and a bad auth URL, and then: 0. Verify no public images in index 1. Attempt to add an image 2. Verify the image does NOT appear in the index output """ self.cleanup() # Start servers with a Swift backend and a bad auth URL override_options = { 'default_store': 'swift', 'swift_store_auth_address': 'badurl', } options = self.__dict__.copy() options.update(override_options) self.start_servers(**options) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Attempt to add an image with tempfile.NamedTemporaryFile() as image_file: image_file.write("XXX") image_file.flush() image_file_name = image_file.name cmd = ("bin/glance --port=%d add name=Jonas is_public=True " "disk_format=qcow2 container_format=bare < %s" % (api_port, image_file_name)) exitcode, out, err = execute(cmd, raise_error=False) self.assertNotEqual(0, exitcode) self.assertTrue('Failed to add image.' in out) # 2. Verify image does not appear as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) self.stop_servers()
def _sync_db(self): with open(self.conf_filepath, 'wb') as conf_file: conf_file.write('[DEFAULT]\n') conf_file.write(self.connection) conf_file.flush() cmd = ('%s -m glance.cmd.manage --config-file %s db sync' % (sys.executable, self.conf_filepath)) execute(cmd, raise_error=True)
def test_add_with_location_and_id(self): self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) image_id = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" # 1a. Add public image cmd = minimal_add_command(api_port, 'MyImage', 'id=%s' % image_id, 'location=http://example.com') exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) expected = 'Added new image with ID: %s' % image_id self.assertTrue(expected in out) # 1b. Add public image with non-uuid id cmd = minimal_add_command(api_port, 'MyImage', 'id=12345', 'location=http://example.com') exitcode, out, err = execute(cmd, expected_exitcode=1) self.assertEqual(1, exitcode) self.assertTrue('Invalid image id format' in out) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) line = lines[0] image_id, name, disk_format, container_format, size = \ [c.strip() for c in line.split()] self.assertEqual('MyImage', name) self.assertEqual('0', size, "Expected image to be 0 bytes in size, " "but got %s. " % size) self.stop_servers()
def _sync_db(self, auto_create): with open(self.conf_filepath, 'wb') as conf_file: conf_file.write('[DEFAULT]\n') conf_file.write('db_auto_create = %r\n' % auto_create) conf_file.write(self.connection) conf_file.flush() cmd = ('bin/glance-manage db_sync --config-file %s' % self.conf_filepath) execute(cmd, raise_error=True)
def _do_test_update_external_source(self, source): self.cleanup() self.start_servers(**self.__dict__.copy()) setup_http(self) api_port = self.api_port registry_port = self.registry_port # 1. Add public image with no image content headers = {'X-Image-Meta-Name': 'MyImage', 'X-Image-Meta-disk_format': 'raw', 'X-Image-Meta-container_format': 'ovf', 'X-Image-Meta-Is-Public': 'True'} path = "http://%s:%d/v1/images" % ("0.0.0.0", api_port) http = httplib2.Http() response, content = http.request(path, 'POST', headers=headers) self.assertEqual(response.status, 201) data = json.loads(content) self.assertEqual(data['image']['name'], 'MyImage') image_id = data['image']['id'] # 2. Update image with external source source = '%s=%s' % (source, get_http_uri(self, 'foobar')) cmd = "bin/glance update %s %s -p %d" % (image_id, source, api_port) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(0, exitcode) self.assertTrue(out.strip().endswith('Updated image %s' % image_id)) # 3. Verify image is now active and of the correct size cmd = "bin/glance --port=%d show %s" % (api_port, image_id) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) expected_lines = [ 'URI: http://0.0.0.0:%s/v1/images/%s' % (api_port, image_id), 'Id: %s' % image_id, 'Public: Yes', 'Name: MyImage', 'Status: active', 'Size: 5120', 'Disk format: raw', 'Container format: ovf', 'Minimum Ram Required (MB): 0', 'Minimum Disk Required (GB): 0', ] lines = out.split("\n") self.assertTrue(set(lines) >= set(expected_lines)) self.stop_servers()
def _do_test_update_external_source(self, source): self.cleanup() self.start_servers(**self.__dict__.copy()) setup_http(self) api_port = self.api_port registry_port = self.registry_port # 1. Add public image with no image content headers = { "X-Image-Meta-Name": "MyImage", "X-Image-Meta-disk_format": "raw", "X-Image-Meta-container_format": "ovf", "X-Image-Meta-Is-Public": "True", } path = "http://%s:%d/v1/images" % ("0.0.0.0", api_port) http = httplib2.Http() response, content = http.request(path, "POST", headers=headers) self.assertEqual(response.status, 201) data = json.loads(content) self.assertEqual(data["image"]["name"], "MyImage") image_id = data["image"]["id"] # 2. Update image with external source source = "%s=%s" % (source, get_http_uri(self, "foobar")) cmd = "bin/glance update %s %s -p %d" % (image_id, source, api_port) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(0, exitcode) self.assertTrue(out.strip().endswith("Updated image %s" % image_id)) # 3. Verify image is now active and of the correct size cmd = "bin/glance --port=%d show %s" % (api_port, image_id) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) expected_lines = [ "URI: http://0.0.0.0:%s/v1/images/%s" % (api_port, image_id), "Id: %s" % image_id, "Public: Yes", "Name: MyImage", "Status: active", "Size: 5120", "Disk format: raw", "Container format: ovf", "Minimum Ram Required (MB): 0", "Minimum Disk Required (GB): 0", ] lines = out.split("\n") self.assertTrue(set(lines) >= set(expected_lines))
def test_show_image_format(self): self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 1. Add public image with tempfile.NamedTemporaryFile() as image_file: image_file.write("XXX") image_file.flush() image_file_name = image_file.name suffix = ' --silent-upload < %s' % image_file_name cmd = minimal_add_command(api_port, 'MyImage', suffix) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) image_id = out.strip().rsplit(' ', 1)[1] # 2. Verify image added as public image cmd = "bin/glance --port=%d show %s" % (api_port, image_id) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[:-1] expected_lines = [ 'URI: http://0.0.0.0:%s/v1/images/%s' % (api_port, image_id), 'Id: %s' % image_id, 'Public: Yes', 'Name: MyImage', 'Status: active', 'Size: 3', 'Disk format: raw', 'Container format: ovf', 'Minimum Ram Required (MB): 0', 'Minimum Disk Required (GB): 0', ] self.assertTrue(set(lines) >= set(expected_lines)) # 3. Delete the image cmd = "bin/glance --port=%d --force delete %s" % (api_port, image_id) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('Deleted image %s' % image_id, out.strip()) self.stop_servers()
def test_add_clear(self): """ We test the following: 1. Add a couple images with metadata 2. Clear the images 3. Verify no public images found 4. Run SQL against DB to verify no undeleted properties """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 1. Add some images for i in range(1, 5): cmd = minimal_add_command(api_port, 'MyImage', 'foo=bar') exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().find('Added new image with ID:') > -1) # 2. Clear all images cmd = "bin/glance --port=%d --force clear" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) # 3. Verify no public images are found cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n") first_line = lines[0] self.assertEqual('', first_line) # 4. Lastly we manually verify with SQL that image properties are # also getting marked as deleted. sql = "SELECT COUNT(*) FROM image_properties WHERE deleted = 0" recs = self.run_sql_cmd(sql) for rec in recs: self.assertEqual(0, rec[0]) self.stop_servers()
def test_add_with_location_and_stdin(self): self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Add public image with tempfile.NamedTemporaryFile() as image_file: image_file.write("XXX") image_file.flush() file_name = image_file.name cmd = minimal_add_command(api_port, 'MyImage', 'location=http://localhost:0 < %s' % file_name) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) line = lines[0] img_info = [c.strip() for c in line.split()] image_id, name, disk_format, container_format, size = img_info self.assertEqual('MyImage', name) self.assertEqual('0', size, "Expected image to be 0 bytes in size, " "but got %s. " % size) self.stop_servers()
def test_cache_index(self): """ Test that cache index command works """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # Verify no cached images cmd = "bin/glance-cache-manage --port=%d list-cached" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue('No cached images' in out.strip()) ids = {} # Add a few images and cache the second one of them # by GETing the image... for x in xrange(0, 4): ids[x] = self.add_image("Image%s" % x) path = "http://%s:%d/v1/images/%s" % ("0.0.0.0", api_port, ids[1]) http = httplib2.Http() response, content = http.request(path, 'GET') self.assertEqual(response.status, 200) self.assertTrue(self.is_image_cached(ids[1]), "%s is not cached." % ids[1]) self.stop_servers()
def test_scrubber_app(self): """ test that the glance-scrubber script runs successfully when not in daemon mode """ self.cleanup() self.start_servers(delayed_delete=True, daemon=False) 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) self.assertEqual(response.status, 201) image = json.loads(content)['image'] self.assertEqual('active', image['status']) image_id = image['id'] 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) 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) # scrub images and make sure they get deleted 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) # 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 immediatly. 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()
def _assert_table_exists(self, db_table): cmd = ("sqlite3 {0} \"SELECT name FROM sqlite_master WHERE " "type='table' AND name='{1}'\"").format(self.db_filepath, db_table) exitcode, out, err = execute(cmd, raise_error=True) msg = "Expected table {0} was not found in the schema".format(db_table) self.assertEqual(out.rstrip().decode("utf-8"), db_table, msg)
def test_add_location_without_checksum(self): """ We test the following: 1. Add an image with location and no checksum 2. Run SQL against DB to verify checksum is NULL """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 1. Add public image cmd = minimal_add_command(api_port, 'MyImage', 'location=http://example.com') exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) image_id = out.split(":")[1].strip() sql = 'SELECT checksum FROM images WHERE id = "%s"' % image_id recs = self.run_sql_cmd(sql) self.assertEqual(None, recs.first()[0]) self.stop_servers()
def _get_children(self): api_pid = open(self.api_server.pid_file).read().strip() cmd = ("ps --no-headers --ppid %s -o pid,cmd | " "grep python | " # NOTE(markwash): ignore non-python procs "awk '{print $1; print >> \"/dev/stderr\"}'" % api_pid) _, out, err = execute(cmd, raise_error=True) return out.strip().split('\n')
def start(self, **kwargs): """ Starts the server. Any kwargs passed to this method will override the configuration value in the conf file used in starting the servers. """ if self.conf_file: raise RuntimeError("Server configuration file already exists!") if not self.conf_base: raise RuntimeError("Subclass did not populate config_base!") conf_override = self.__dict__.copy() if kwargs: conf_override.update(**kwargs) # A config file to use just for this test...we don't want # to trample on currently-running Glance servers, now do we? conf_file = tempfile.NamedTemporaryFile() conf_file.write(self.conf_base % conf_override) conf_file.flush() self.conf_file = conf_file self.conf_file_name = conf_file.name cmd = "./bin/glance-control %(server_name)s start " "%(conf_file_name)s --pid-file=%(pid_file)s" % self.__dict__ return execute(cmd)
def _reset_database(self, conn_string): conn_pieces = urlparse.urlparse(conn_string) if conn_string.startswith("sqlite"): # We can just delete the SQLite database, which is # the easiest and cleanest solution db_path = conn_pieces.path.strip("/") if db_path and os.path.exists(db_path): os.unlink(db_path) # No need to recreate the SQLite DB. SQLite will # create it for us if it's not there... elif conn_string.startswith("mysql"): # We can execute the MySQL client to destroy and re-create # the MYSQL database, which is easier and less error-prone # than using SQLAlchemy to do this via MetaData...trust me. database = conn_pieces.path.strip("/") loc_pieces = conn_pieces.netloc.split("@") host = loc_pieces[1] auth_pieces = loc_pieces[0].split(":") user = auth_pieces[0] password = "" if len(auth_pieces) > 1: if auth_pieces[1].strip(): password = "******" % auth_pieces[1] sql = ("drop database if exists %(database)s; " "create database %(database)s;") % locals() cmd = ("mysql -u%(user)s %(password)s -h%(host)s " '-e"%(sql)s"') % locals() exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode)
def stop(self): """ Spin down the server. """ cmd = ("%(server_control)s %(server_name)s stop " "%(conf_file_name)s --pid-file=%(pid_file)s" % self.__dict__) return execute(cmd, no_venv=self.no_venv, exec_env=self.exec_env)
def _reset_databases(self): for key, engine in self.engines.items(): conn_string = TestMigrations.TEST_DATABASES[key] conn_pieces = urlparse.urlparse(conn_string) if conn_string.startswith('sqlite'): # We can just delete the SQLite database, which is # the easiest and cleanest solution db_path = conn_pieces.path.strip('/') if os.path.exists(db_path): os.unlink(db_path) # No need to recreate the SQLite DB. SQLite will # create it for us if it's not there... elif conn_string.startswith('mysql'): # We can execute the MySQL client to destroy and re-create # the MYSQL database, which is easier and less error-prone # than using SQLAlchemy to do this via MetaData...trust me. database = conn_pieces.path.strip('/') loc_pieces = conn_pieces.netloc.split('@') host = loc_pieces[1] auth_pieces = loc_pieces[0].split(':') user = auth_pieces[0] password = "" if len(auth_pieces) > 1: if auth_pieces[1].strip(): password = "******" % auth_pieces[1] sql = ("drop database if exists %(database)s; " "create database %(database)s;") % locals() cmd = ("mysql -u%(user)s %(password)s -h%(host)s " "-e\"%(sql)s\"") % locals() exitcode, out, err = utils.execute(cmd) self.assertEqual(0, exitcode)
def test_cache_index(self): """ Test that cache index command works """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port # Verify no cached images exe_cmd = "%s -m glance.cmd.cache_manage" % sys.executable cmd = "%s --port=%d list-cached" % (exe_cmd, api_port) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertIn("No cached images", out.strip()) ids = {} # Add a few images and cache the second one of them # by GETing the image... for x in range(4): ids[x] = self.add_image("Image%s" % x) path = "http://%s:%d/v1/images/%s" % ("127.0.0.1", api_port, ids[1]) http = httplib2.Http() response, content = http.request(path, "GET") self.assertEqual(200, response.status) self.assertTrue(self.is_image_cached(ids[1]), "%s is not cached." % ids[1]) self.assertTrue(self.iso_date(ids[1])) self.stop_servers()
def _assert_table_exists(self, db_table): cmd = ("sqlite3 {0} \"SELECT name FROM sqlite_master WHERE " "type='table' AND name='{1}'\"").format(self.db_filepath, db_table) exitcode, out, err = execute(cmd, raise_error=True) msg = "Expected table {0} was not found in the schema".format(db_table) self.assertEqual(out.rstrip(), db_table, msg)
def test_timeout(self): self.cleanup() keep_sleeping = True #start a simple HTTP server in a thread that hangs for a bit class RemoteImageHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): cnt = 1 while (keep_sleeping): cnt += 1 time.sleep(0.1) if cnt > 100: break server_class = BaseHTTPServer.HTTPServer local_server = server_class(('127.0.0.1', 0), RemoteImageHandler) local_ip, local_port = local_server.server_address def serve_requests(httpd): httpd.serve_forever() thread.start_new_thread(serve_requests, (local_server,)) cmd = ("bin/glance --port=%d index --timeout=1") % local_port exitcode, out, err = execute(cmd, raise_error=False) keep_sleeping = False local_server.shutdown() self.assertNotEqual(0, exitcode) self.assertTrue("timed out" in out)
def test_scrubber_app_queue_errors_not_daemon(self): """ test that the glance-scrubber exits with an exit code > 0 when it fails to lookup images, indicating a configuration error when not in daemon mode. Related-Bug: #1548289 """ # Don't start the registry server to cause intended failure # Don't start the api server to save time exitcode, out, err = self.scrubber_daemon.start( delayed_delete=True, daemon=False) self.assertEqual(0, exitcode, "Failed to spin up the Scrubber daemon. " "Got: %s" % err) # Run the Scrubber exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s" % (exe_cmd, self.scrubber_daemon.conf_file_name)) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(1, exitcode) self.assertIn('Can not get scrub jobs from queue', str(err)) self.stop_server(self.scrubber_daemon)
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." self.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 wait_for_scrub(path) self.stop_servers()
def test_timeout(self): self.cleanup() keep_sleeping = True #start a simple HTTP server in a thread that hangs for a bit class RemoteImageHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): cnt = 1 while (keep_sleeping): cnt += 1 time.sleep(0.1) if cnt > 100: break server_class = BaseHTTPServer.HTTPServer local_server = server_class(('127.0.0.1', 0), RemoteImageHandler) local_ip, local_port = local_server.server_address def serve_requests(httpd): httpd.serve_forever() thread.start_new_thread(serve_requests, (local_server, )) cmd = ("bin/glance --port=%d index --timeout=1") % local_port exitcode, out, err = execute(cmd, raise_error=False) keep_sleeping = False local_server.shutdown() self.assertNotEqual(0, exitcode) self.assertTrue("timed out" in out)
def test_scrubber_app_queue_errors_not_daemon(self): """ test that the glance-scrubber exits with an exit code > 0 when it fails to lookup images, indicating a configuration error when not in daemon mode. Related-Bug: #1548289 """ # Don't start the registry server to cause intended failure # Don't start the api server to save time exitcode, out, err = self.scrubber_daemon.start( delayed_delete=True, daemon=False, registry_port=28890) self.assertEqual(0, exitcode, "Failed to spin up the Scrubber daemon. " "Got: %s" % err) # Run the Scrubber exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s" % (exe_cmd, self.scrubber_daemon.conf_file_name)) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(1, exitcode) self.assertIn('Can not get scrub jobs from queue', err) self.stop_server(self.scrubber_daemon, 'Scrubber daemon')
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." self.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 = ("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 self.wait_for_scrub(path) self.stop_servers()
def test_add_clear(self): """ We test the following: 1. Add a couple images with metadata 2. Clear the images 3. Verify no public images found 4. Run SQL against DB to verify no undeleted properties """ self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 1. Add some images for i in range(1, 5): cmd = "bin/glance --port=%d add is_public=True name=MyName " \ " foo=bar" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().find('Added new image with ID:') > -1) # 2. Clear all images cmd = "bin/glance --port=%d --force clear" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) # 3. Verify no public images are found cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n") first_line = lines[0] self.assertEqual('', first_line) # 4. Lastly we manually verify with SQL that image properties are # also getting marked as deleted. sql = "SELECT COUNT(*) FROM image_properties WHERE deleted = 0" recs = self.run_sql_cmd(sql) for rec in recs: self.assertEqual(0, rec[0]) self.stop_servers()
def test_scrubber_delete_handles_exception(self): """ Test that the scrubber handles the case where an exception occurs when _delete() is called. The scrubber should not write out queue files in this case. """ # Start servers. self.cleanup() kwargs = self.__dict__.copy() kwargs['use_user_token'] = True self.start_servers(delayed_delete=True, daemon=False, default_store='file', **kwargs) # Check that we are using a file backend. self.assertEqual(self.api_server.default_store, 'file') # add an image path = "http://%s:%d/v2/images" % ("127.0.0.1", self.api_port) response, content = self._send_create_image_http_request(path) self.assertEqual(http_client.CREATED, response.status) image = jsonutils.loads(content) self.assertEqual('queued', image['status']) file_path = "%s/%s/file" % (path, image['id']) response, content = self._send_upload_image_http_request(file_path, body='XXX') self.assertEqual(http_client.NO_CONTENT, response.status) path = "%s/%s" % (path, image['id']) response, content = self._send_http_request(path, 'GET') image = jsonutils.loads(content) self.assertEqual('active', image['status']) # delete the image response, content = self._send_http_request(path, 'DELETE') self.assertEqual(http_client.NO_CONTENT, response.status) # ensure the image is marked pending delete. image = self._get_pending_delete_image(image['id']) self.assertEqual('pending_delete', image['status']) # Remove the file from the backend. file_path = os.path.join(self.api_server.image_dir, image['id']) os.remove(file_path) # Wait for the scrub time on the image to pass time.sleep(self.api_server.scrub_time) # run the scrubber app, and ensure it doesn't fall over exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s" % (exe_cmd, self.scrubber_daemon.conf_file_name)) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(0, exitcode) self.wait_for_scrub(image['id']) self.stop_servers()
def test_add_with_location_and_stdin(self): self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Add public image with tempfile.NamedTemporaryFile() as image_file: image_file.write("XXX") image_file.flush() file_name = image_file.name cmd = minimal_add_command( api_port, 'MyImage', 'location=http://example.com < %s' % file_name) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) line = lines[0] img_info = [c.strip() for c in line.split()] image_id, name, disk_format, container_format, size = img_info self.assertEqual('MyImage', name) self.assertEqual( '0', size, "Expected image to be 0 bytes in size, " "but got %s. " % size)
def _assert_tables(self): cmd = "sqlite3 %s '.schema'" % self.db_filepath exitcode, out, err = execute(cmd, raise_error=True) self.assertTrue('CREATE TABLE images' in out) self.assertTrue('CREATE TABLE image_tags' in out) self.assertTrue('CREATE TABLE image_members' in out) self.assertTrue('CREATE TABLE image_properties' in out)
def stop(self): """ Spin down the server. """ cmd = ("./bin/glance-control %(server_name)s stop " "%(conf_file_name)s --pid-file=%(pid_file)s" % self.__dict__) return execute(cmd)
def test_scrubber_app_with_trustedauth_registry(self): """ test that the glance-scrubber script runs successfully when not in daemon mode and with a registry that operates in trustedauth mode """ self.cleanup() self.api_server.deployment_flavor = 'noauth' self.registry_server.deployment_flavor = 'trusted-auth' self.start_servers(delayed_delete=True, daemon=False, metadata_encryption_key='', send_identity_headers=True) base_headers = { 'X-Identity-Status': 'Confirmed', 'X-Auth-Token': '932c5c84-02ac-4fe5-a9ba-620af0e2bb96', 'X-User-Id': 'f9a41d13-0c13-47e9-bee2-ce4e8bfe958e', 'X-Tenant-Id': 'deae8923-075d-4287-924b-840fb2644874', 'X-Roles': 'admin', } 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', } headers.update(base_headers) 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) self.assertEqual(http_client.CREATED, response.status) image = jsonutils.loads(content)['image'] self.assertEqual('active', image['status']) image_id = image['id'] 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', headers=base_headers) self.assertEqual(http_client.OK, response.status) response, content = http.request(path, 'HEAD', headers=base_headers) self.assertEqual(http_client.OK, response.status) 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) # scrub images and make sure they get deleted exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s" % (exe_cmd, self.scrubber_daemon.conf_file_name)) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(0, exitcode) self.wait_for_scrub(path, headers=base_headers) self.stop_servers()
def test_add_copying_from(self): self.cleanup() self.start_servers(**self.__dict__.copy()) setup_http(self) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Add public image suffix = 'copy_from=%s' % get_http_uri(self, 'foobar') cmd = minimal_add_command(api_port, 'MyImage', suffix) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) line = lines[0] image_id, name, disk_format, container_format, size = \ [c.strip() for c in line.split()] self.assertEqual('MyImage', name) self.assertEqual( '5120', size, "Expected image to be 5120 bytes " " in size, but got %s. " % size) self.stop_servers()
def _test_content(): exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s --restore %s" % (exe_cmd, self.scrubber_daemon.conf_file_name, image['id'])) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(1, exitcode) self.assertIn( 'cannot restore the image from active to active ' '(wanted from_state=pending_delete)', str(err))
def _db_command(self, db_method): with open(self.conf_filepath, 'w') as conf_file: conf_file.write('[DEFAULT]\n') conf_file.write(self.connection) conf_file.flush() cmd = ('%s -m glance.cmd.manage --config-file %s db %s' % (sys.executable, self.conf_filepath, db_method)) return execute(cmd, raise_error=True)
def iso_date(self, image_id): """ Return True if supplied image ID is cached, False otherwise """ cmd = "glance-cache-manage --port=%d list-cached" % self.api_port exitcode, out, err = execute(cmd) return datetime.datetime.utcnow().strftime("%Y-%m-%d") in out
def test_index_with_https(self): self.cleanup() self.start_servers(**self.__dict__.copy()) cmd = ("bin/glance -N https://auth/ --port=%d index") % self.api_port exitcode, out, err = execute(cmd, raise_error=False) self.assertNotEqual(0, exitcode) self._assertNotIn('SSL23_GET_SERVER_HELLO', out)
def test_scrubber_restore_image_with_daemon_raise_error(self): exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --daemon --restore fake_image_id" % exe_cmd) exitcode, out, err = execute(cmd, raise_error=False) self.assertEqual(1, exitcode) self.assertIn('The restore and daemon options should not be set ' 'together', str(err))
def _verify_owner(self, owner, image_id): cmd = "bin/glance --port=%d show %s" % (self.api_port, image_id) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) # verify expected owner as first class attribute self.assertTrue(('Owner: %s' % owner) in out) # ensure owner does not appear as a custom property self.assertFalse("Property 'owner':" in out)
def _create_by_admin(self, owner): # ownership set by admin user (defaults as such due to no-auth) cmd = minimal_add_command(self.api_port, 'MyImage', '--silent-upload owner=%s' % owner) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) return out.strip().replace('Added new image with ID: ', '')
def test_compare(self): # Test for issue: https://bugs.launchpad.net/glance/+bug/1598928 cmd = ('%s -m glance.cmd.replicator ' 'compare az1:9292 az2:9292 --debug' % (sys.executable,)) exitcode, out, err = execute(cmd, raise_error=False) self.assertIn( 'Request: GET http://az1:9292/v1/images/detail?is_public=None', err )
def _test_content(): scrubber = functional.ScrubberDaemon(self.test_dir, self.policy_file) scrubber.write_conf(daemon=False) scrubber.needs_database = True scrubber.create_database() exe_cmd = "%s -m glance.cmd.scrubber" % sys.executable cmd = ("%s --config-file %s --restore fake_image_id" % (exe_cmd, scrubber.conf_file_name)) return execute(cmd, raise_error=False)
def iso_date(self, image_id): """ Return True if supplied image ID is cached, False otherwise """ exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable cmd = "%s --port=%d list-cached" % (exe_cmd, self.api_port) exitcode, out, err = execute(cmd) return datetime.datetime.utcnow().strftime("%Y-%m-%d") in out
def is_image_cached(self, image_id): """ Return True if supplied image ID is cached, False otherwise """ cmd = "bin/glance-cache-manage --port=%d list-cached" % self.api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) return image_id in out
def test_add_no_name(self): self.cleanup() self.start_servers(**self.__dict__.copy()) api_port = self.api_port registry_port = self.registry_port # 0. Verify no public images cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertEqual('', out.strip()) # 1. Add public image # Can't use minimal_add_command since that uses # name... cmd = ("bin/glance --port=%d add is_public=True" " disk_format=raw container_format=ovf" " %s" % (api_port, 'location=http://localhost:0')) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) self.assertTrue(out.strip().startswith('Added new image with ID:')) # 2. Verify image added as public image cmd = "bin/glance --port=%d index" % api_port exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) lines = out.split("\n")[2:-1] self.assertEqual(1, len(lines)) line = lines[0] image_id, name, disk_format, container_format, size = \ [c.strip() for c in line.split()] self.assertEqual('None', name) self.stop_servers()
def is_image_cached(self, image_id): """ Return True if supplied image ID is cached, False otherwise """ exe_cmd = '%s -m glance.cmd.cache_manage' % sys.executable cmd = "%s --port=%d list-cached" % (exe_cmd, self.api_port) exitcode, out, err = execute(cmd) self.assertEqual(0, exitcode) return image_id in out
def test_big_int_mapping(self): """Ensure BigInteger not mapped to BIGINT""" self.cleanup() self.start_servers(**self.__dict__.copy()) cmd = 'sqlite3 tests.sqlite ".schema"' exitcode, out, err = execute(cmd, raise_error=True) self.assertNotIn('BIGINT', out) self.stop_servers()